mirror of
https://github.com/kubernetes/node-problem-detector.git
synced 2026-02-14 18:09:57 +00:00
Add Godeps
This commit is contained in:
691
Godeps/Godeps.json
generated
Normal file
691
Godeps/Godeps.json
generated
Normal file
@@ -0,0 +1,691 @@
|
||||
{
|
||||
"ImportPath": "k8s.io/node-problem-detector",
|
||||
"GoVersion": "go1.6",
|
||||
"GodepVersion": "v63",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "bitbucket.org/ww/goautoneg",
|
||||
"Comment": "null-5",
|
||||
"Rev": "'75cd24fc2f2c2a2088577d12123ddee5f54e0675'"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/beorn7/perks/quantile",
|
||||
"Rev": "b965b613227fddccbfffe13eae360ed3fa822f8d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/blang/semver",
|
||||
"Comment": "v3.0.1",
|
||||
"Rev": "31b736133b98f26d5e078ec9eb591666edfd091f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/davecgh/go-spew/spew",
|
||||
"Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/distribution/digest",
|
||||
"Comment": "v2.4.0-rc.1-38-gcd27f17",
|
||||
"Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/distribution/reference",
|
||||
"Comment": "v2.4.0-rc.1-38-gcd27f17",
|
||||
"Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/docker/pkg/mount",
|
||||
"Comment": "v1.4.1-4831-g0f5c9d3",
|
||||
"Rev": "0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/go-units",
|
||||
"Comment": "v0.1.0-21-g0bbddae",
|
||||
"Rev": "0bbddae09c5a5419a8c6dcdd7ff90da3d450393b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/emicklei/go-restful",
|
||||
"Comment": "v1.2-34-g496d495",
|
||||
"Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/emicklei/go-restful/log",
|
||||
"Comment": "v1.2-34-g496d495",
|
||||
"Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/emicklei/go-restful/swagger",
|
||||
"Comment": "v1.2-34-g496d495",
|
||||
"Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ghodss/yaml",
|
||||
"Rev": "73d445a93680fa1a78ae23a5839bad48f32ba1ee"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||
"Comment": "v0.1-125-g82d16f7",
|
||||
"Rev": "82d16f734d6d871204a3feb1a73cb220cc92574c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/glog",
|
||||
"Rev": "44145f04b68cf362d9c4df2182967c2275eaefed"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/groupcache/lru",
|
||||
"Rev": "604ed5785183e59ae2789449d89e73f3a2a77987"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "b982704f8bb716bb608144408cff30e15fbde841"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/gofuzz",
|
||||
"Rev": "bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hpcloud/tail",
|
||||
"Comment": "v1.0.0-35-ga30252c",
|
||||
"Rev": "a30252cb686a21eb2d0b98132633053ec2f7f1e5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hpcloud/tail/ratelimiter",
|
||||
"Comment": "v1.0.0-35-ga30252c",
|
||||
"Rev": "a30252cb686a21eb2d0b98132633053ec2f7f1e5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hpcloud/tail/util",
|
||||
"Comment": "v1.0.0-35-ga30252c",
|
||||
"Rev": "a30252cb686a21eb2d0b98132633053ec2f7f1e5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hpcloud/tail/watch",
|
||||
"Comment": "v1.0.0-35-ga30252c",
|
||||
"Rev": "a30252cb686a21eb2d0b98132633053ec2f7f1e5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hpcloud/tail/winfile",
|
||||
"Comment": "v1.0.0-35-ga30252c",
|
||||
"Rev": "a30252cb686a21eb2d0b98132633053ec2f7f1e5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/juju/ratelimit",
|
||||
"Rev": "77ed1c8a01217656d2080ad51981f6e99adaa177"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||
"Rev": "fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups",
|
||||
"Comment": "v0.0.7",
|
||||
"Rev": "7ca2aa4873aea7cb4265b1726acb24b90d8726c6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/fs",
|
||||
"Comment": "v0.0.7",
|
||||
"Rev": "7ca2aa4873aea7cb4265b1726acb24b90d8726c6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/opencontainers/runc/libcontainer/configs",
|
||||
"Comment": "v0.0.7",
|
||||
"Rev": "7ca2aa4873aea7cb4265b1726acb24b90d8726c6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/opencontainers/runc/libcontainer/system",
|
||||
"Comment": "v0.0.7",
|
||||
"Rev": "7ca2aa4873aea7cb4265b1726acb24b90d8726c6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pborman/uuid",
|
||||
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/client_golang/prometheus",
|
||||
"Comment": "0.7.0-39-g3b78d7a",
|
||||
"Rev": "3b78d7a77f51ccbc364d4bc170920153022cfd08"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/client_model/go",
|
||||
"Comment": "model-0.0.2-12-gfa8ad6f",
|
||||
"Rev": "fa8ad6fec33561be4280a8f0514318c79d7f6cb6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/common/expfmt",
|
||||
"Rev": "ef7a9a5fb138aa5d3a19988537606226869a0390"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/common/model",
|
||||
"Rev": "ef7a9a5fb138aa5d3a19988537606226869a0390"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/procfs",
|
||||
"Rev": "490cc6eb5fa45bf8a8b7b73c8bc82a8160e8531d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/pflag",
|
||||
"Rev": "08b1a584251b5b62f458943640fc8ebd4d50aaa5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ugorji/go/codec",
|
||||
"Rev": "f4485b318aadd133842532f841dc205a8e339d74"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/fsnotify.v1",
|
||||
"Comment": "v1.3.0",
|
||||
"Rev": "30411dbcefb7a1da7e84f75530ad3abe4011b4f8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/inf.v0",
|
||||
"Rev": "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/tomb.v1",
|
||||
"Rev": "dd632973f1e7218eb1089048e0798ec9ae7dceb8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/yaml.v2",
|
||||
"Rev": "a83829b6f1293c91addabc89d0571c246397bbf4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/endpoints",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/errors",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/install",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/meta",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/pod",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/resource",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/service",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/unversioned",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/unversioned/validation",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/util",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/v1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/api/validation",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apimachinery",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apimachinery/registered",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/apps",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/apps/install",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/apps/v1alpha1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/authorization",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/authorization/install",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/autoscaling",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/autoscaling/install",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/autoscaling/v1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/batch",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/batch/install",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/batch/v1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/componentconfig",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/componentconfig/install",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/extensions",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/extensions/install",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/metrics",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/metrics/install",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/metrics/v1alpha1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/policy",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/policy/install",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/apis/policy/v1alpha1",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/auth/user",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/capabilities",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/autoscaling/unversioned",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/autoscaling/unversioned/fake",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/unversioned",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/unversioned/fake",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned/fake",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned/fake",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/metrics",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/record",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/restclient",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/testing/core",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/transport",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/typed/discovery",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/typed/discovery/fake",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/conversion",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/conversion/queryparams",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/fields",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/kubelet/qos",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/labels",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/master/ports",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/runtime",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/json",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/protobuf",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/recognizer",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/streaming",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/versioning",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/types",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/crypto",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/errors",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/flowcontrol",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/framer",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/hash",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/integer",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/intstr",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/json",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/net",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/net/sets",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/parsers",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/rand",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/runtime",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/sets",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/strategicpatch",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/validation",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/validation/field",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/wait",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/util/yaml",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/version",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/watch",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/pkg/watch/versioned",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/third_party/forked/json",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kubernetes/third_party/forked/reflect",
|
||||
"Comment": "v1.3.0-alpha.4-9-g088a683",
|
||||
"Rev": "088a683a2c8ddc775ae12c7276beb709416092a2"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
Godeps/Readme
generated
Normal file
5
Godeps/Readme
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
||||
13
vendor/bitbucket.org/ww/goautoneg/Makefile
generated
vendored
Normal file
13
vendor/bitbucket.org/ww/goautoneg/Makefile
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG=bitbucket.org/ww/goautoneg
|
||||
GOFILES=autoneg.go
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
||||
format:
|
||||
gofmt -w *.go
|
||||
|
||||
docs:
|
||||
gomake clean
|
||||
godoc ${TARG} > README.txt
|
||||
67
vendor/bitbucket.org/ww/goautoneg/README.txt
generated
vendored
Normal file
67
vendor/bitbucket.org/ww/goautoneg/README.txt
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
PACKAGE
|
||||
|
||||
package goautoneg
|
||||
import "bitbucket.org/ww/goautoneg"
|
||||
|
||||
HTTP Content-Type Autonegotiation.
|
||||
|
||||
The functions in this package implement the behaviour specified in
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||
|
||||
Copyright (c) 2011, Open Knowledge Foundation Ltd.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
Neither the name of the Open Knowledge Foundation Ltd. nor the
|
||||
names of its contributors may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
|
||||
func Negotiate(header string, alternatives []string) (content_type string)
|
||||
Negotiate the most appropriate content_type given the accept header
|
||||
and a list of alternatives.
|
||||
|
||||
func ParseAccept(header string) (accept []Accept)
|
||||
Parse an Accept Header string returning a sorted list
|
||||
of clauses
|
||||
|
||||
|
||||
TYPES
|
||||
|
||||
type Accept struct {
|
||||
Type, SubType string
|
||||
Q float32
|
||||
Params map[string]string
|
||||
}
|
||||
Structure to represent a clause in an HTTP Accept Header
|
||||
|
||||
|
||||
SUBDIRECTORIES
|
||||
|
||||
.hg
|
||||
162
vendor/bitbucket.org/ww/goautoneg/autoneg.go
generated
vendored
Normal file
162
vendor/bitbucket.org/ww/goautoneg/autoneg.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
HTTP Content-Type Autonegotiation.
|
||||
|
||||
The functions in this package implement the behaviour specified in
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||
|
||||
Copyright (c) 2011, Open Knowledge Foundation Ltd.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
Neither the name of the Open Knowledge Foundation Ltd. nor the
|
||||
names of its contributors may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
*/
|
||||
package goautoneg
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Structure to represent a clause in an HTTP Accept Header
|
||||
type Accept struct {
|
||||
Type, SubType string
|
||||
Q float64
|
||||
Params map[string]string
|
||||
}
|
||||
|
||||
// For internal use, so that we can use the sort interface
|
||||
type accept_slice []Accept
|
||||
|
||||
func (accept accept_slice) Len() int {
|
||||
slice := []Accept(accept)
|
||||
return len(slice)
|
||||
}
|
||||
|
||||
func (accept accept_slice) Less(i, j int) bool {
|
||||
slice := []Accept(accept)
|
||||
ai, aj := slice[i], slice[j]
|
||||
if ai.Q > aj.Q {
|
||||
return true
|
||||
}
|
||||
if ai.Type != "*" && aj.Type == "*" {
|
||||
return true
|
||||
}
|
||||
if ai.SubType != "*" && aj.SubType == "*" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (accept accept_slice) Swap(i, j int) {
|
||||
slice := []Accept(accept)
|
||||
slice[i], slice[j] = slice[j], slice[i]
|
||||
}
|
||||
|
||||
// Parse an Accept Header string returning a sorted list
|
||||
// of clauses
|
||||
func ParseAccept(header string) (accept []Accept) {
|
||||
parts := strings.Split(header, ",")
|
||||
accept = make([]Accept, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
part := strings.Trim(part, " ")
|
||||
|
||||
a := Accept{}
|
||||
a.Params = make(map[string]string)
|
||||
a.Q = 1.0
|
||||
|
||||
mrp := strings.Split(part, ";")
|
||||
|
||||
media_range := mrp[0]
|
||||
sp := strings.Split(media_range, "/")
|
||||
a.Type = strings.Trim(sp[0], " ")
|
||||
|
||||
switch {
|
||||
case len(sp) == 1 && a.Type == "*":
|
||||
a.SubType = "*"
|
||||
case len(sp) == 2:
|
||||
a.SubType = strings.Trim(sp[1], " ")
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if len(mrp) == 1 {
|
||||
accept = append(accept, a)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, param := range mrp[1:] {
|
||||
sp := strings.SplitN(param, "=", 2)
|
||||
if len(sp) != 2 {
|
||||
continue
|
||||
}
|
||||
token := strings.Trim(sp[0], " ")
|
||||
if token == "q" {
|
||||
a.Q, _ = strconv.ParseFloat(sp[1], 32)
|
||||
} else {
|
||||
a.Params[token] = strings.Trim(sp[1], " ")
|
||||
}
|
||||
}
|
||||
|
||||
accept = append(accept, a)
|
||||
}
|
||||
|
||||
slice := accept_slice(accept)
|
||||
sort.Sort(slice)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Negotiate the most appropriate content_type given the accept header
|
||||
// and a list of alternatives.
|
||||
func Negotiate(header string, alternatives []string) (content_type string) {
|
||||
asp := make([][]string, 0, len(alternatives))
|
||||
for _, ctype := range alternatives {
|
||||
asp = append(asp, strings.SplitN(ctype, "/", 2))
|
||||
}
|
||||
for _, clause := range ParseAccept(header) {
|
||||
for i, ctsp := range asp {
|
||||
if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
|
||||
content_type = alternatives[i]
|
||||
return
|
||||
}
|
||||
if clause.Type == ctsp[0] && clause.SubType == "*" {
|
||||
content_type = alternatives[i]
|
||||
return
|
||||
}
|
||||
if clause.Type == "*" && clause.SubType == "*" {
|
||||
content_type = alternatives[i]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
2388
vendor/github.com/beorn7/perks/quantile/exampledata.txt
generated
vendored
Normal file
2388
vendor/github.com/beorn7/perks/quantile/exampledata.txt
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
292
vendor/github.com/beorn7/perks/quantile/stream.go
generated
vendored
Normal file
292
vendor/github.com/beorn7/perks/quantile/stream.go
generated
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
// Package quantile computes approximate quantiles over an unbounded data
|
||||
// stream within low memory and CPU bounds.
|
||||
//
|
||||
// A small amount of accuracy is traded to achieve the above properties.
|
||||
//
|
||||
// Multiple streams can be merged before calling Query to generate a single set
|
||||
// of results. This is meaningful when the streams represent the same type of
|
||||
// data. See Merge and Samples.
|
||||
//
|
||||
// For more detailed information about the algorithm used, see:
|
||||
//
|
||||
// Effective Computation of Biased Quantiles over Data Streams
|
||||
//
|
||||
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
|
||||
package quantile
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Sample holds an observed value and meta information for compression. JSON
|
||||
// tags have been added for convenience.
|
||||
type Sample struct {
|
||||
Value float64 `json:",string"`
|
||||
Width float64 `json:",string"`
|
||||
Delta float64 `json:",string"`
|
||||
}
|
||||
|
||||
// Samples represents a slice of samples. It implements sort.Interface.
|
||||
type Samples []Sample
|
||||
|
||||
func (a Samples) Len() int { return len(a) }
|
||||
func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
|
||||
func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
type invariant func(s *stream, r float64) float64
|
||||
|
||||
// NewLowBiased returns an initialized Stream for low-biased quantiles
|
||||
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||
// error guarantees can still be given even for the lower ranks of the data
|
||||
// distribution.
|
||||
//
|
||||
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||
// returned by a query is guaranteed to be within (1±Epsilon)*Quantile.
|
||||
//
|
||||
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||
// properties.
|
||||
func NewLowBiased(epsilon float64) *Stream {
|
||||
ƒ := func(s *stream, r float64) float64 {
|
||||
return 2 * epsilon * r
|
||||
}
|
||||
return newStream(ƒ)
|
||||
}
|
||||
|
||||
// NewHighBiased returns an initialized Stream for high-biased quantiles
|
||||
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||
// error guarantees can still be given even for the higher ranks of the data
|
||||
// distribution.
|
||||
//
|
||||
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||
// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile).
|
||||
//
|
||||
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||
// properties.
|
||||
func NewHighBiased(epsilon float64) *Stream {
|
||||
ƒ := func(s *stream, r float64) float64 {
|
||||
return 2 * epsilon * (s.n - r)
|
||||
}
|
||||
return newStream(ƒ)
|
||||
}
|
||||
|
||||
// NewTargeted returns an initialized Stream concerned with a particular set of
|
||||
// quantile values that are supplied a priori. Knowing these a priori reduces
|
||||
// space and computation time. The targets map maps the desired quantiles to
|
||||
// their absolute errors, i.e. the true quantile of a value returned by a query
|
||||
// is guaranteed to be within (Quantile±Epsilon).
|
||||
//
|
||||
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
|
||||
func NewTargeted(targets map[float64]float64) *Stream {
|
||||
ƒ := func(s *stream, r float64) float64 {
|
||||
var m = math.MaxFloat64
|
||||
var f float64
|
||||
for quantile, epsilon := range targets {
|
||||
if quantile*s.n <= r {
|
||||
f = (2 * epsilon * r) / quantile
|
||||
} else {
|
||||
f = (2 * epsilon * (s.n - r)) / (1 - quantile)
|
||||
}
|
||||
if f < m {
|
||||
m = f
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
return newStream(ƒ)
|
||||
}
|
||||
|
||||
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
|
||||
// design. Take care when using across multiple goroutines.
|
||||
type Stream struct {
|
||||
*stream
|
||||
b Samples
|
||||
sorted bool
|
||||
}
|
||||
|
||||
func newStream(ƒ invariant) *Stream {
|
||||
x := &stream{ƒ: ƒ}
|
||||
return &Stream{x, make(Samples, 0, 500), true}
|
||||
}
|
||||
|
||||
// Insert inserts v into the stream.
|
||||
func (s *Stream) Insert(v float64) {
|
||||
s.insert(Sample{Value: v, Width: 1})
|
||||
}
|
||||
|
||||
func (s *Stream) insert(sample Sample) {
|
||||
s.b = append(s.b, sample)
|
||||
s.sorted = false
|
||||
if len(s.b) == cap(s.b) {
|
||||
s.flush()
|
||||
}
|
||||
}
|
||||
|
||||
// Query returns the computed qth percentiles value. If s was created with
|
||||
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
|
||||
// will return an unspecified result.
|
||||
func (s *Stream) Query(q float64) float64 {
|
||||
if !s.flushed() {
|
||||
// Fast path when there hasn't been enough data for a flush;
|
||||
// this also yields better accuracy for small sets of data.
|
||||
l := len(s.b)
|
||||
if l == 0 {
|
||||
return 0
|
||||
}
|
||||
i := int(float64(l) * q)
|
||||
if i > 0 {
|
||||
i -= 1
|
||||
}
|
||||
s.maybeSort()
|
||||
return s.b[i].Value
|
||||
}
|
||||
s.flush()
|
||||
return s.stream.query(q)
|
||||
}
|
||||
|
||||
// Merge merges samples into the underlying streams samples. This is handy when
|
||||
// merging multiple streams from separate threads, database shards, etc.
|
||||
//
|
||||
// ATTENTION: This method is broken and does not yield correct results. The
|
||||
// underlying algorithm is not capable of merging streams correctly.
|
||||
func (s *Stream) Merge(samples Samples) {
|
||||
sort.Sort(samples)
|
||||
s.stream.merge(samples)
|
||||
}
|
||||
|
||||
// Reset reinitializes and clears the list reusing the samples buffer memory.
|
||||
func (s *Stream) Reset() {
|
||||
s.stream.reset()
|
||||
s.b = s.b[:0]
|
||||
}
|
||||
|
||||
// Samples returns stream samples held by s.
|
||||
func (s *Stream) Samples() Samples {
|
||||
if !s.flushed() {
|
||||
return s.b
|
||||
}
|
||||
s.flush()
|
||||
return s.stream.samples()
|
||||
}
|
||||
|
||||
// Count returns the total number of samples observed in the stream
|
||||
// since initialization.
|
||||
func (s *Stream) Count() int {
|
||||
return len(s.b) + s.stream.count()
|
||||
}
|
||||
|
||||
func (s *Stream) flush() {
|
||||
s.maybeSort()
|
||||
s.stream.merge(s.b)
|
||||
s.b = s.b[:0]
|
||||
}
|
||||
|
||||
func (s *Stream) maybeSort() {
|
||||
if !s.sorted {
|
||||
s.sorted = true
|
||||
sort.Sort(s.b)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) flushed() bool {
|
||||
return len(s.stream.l) > 0
|
||||
}
|
||||
|
||||
type stream struct {
|
||||
n float64
|
||||
l []Sample
|
||||
ƒ invariant
|
||||
}
|
||||
|
||||
func (s *stream) reset() {
|
||||
s.l = s.l[:0]
|
||||
s.n = 0
|
||||
}
|
||||
|
||||
func (s *stream) insert(v float64) {
|
||||
s.merge(Samples{{v, 1, 0}})
|
||||
}
|
||||
|
||||
func (s *stream) merge(samples Samples) {
|
||||
// TODO(beorn7): This tries to merge not only individual samples, but
|
||||
// whole summaries. The paper doesn't mention merging summaries at
|
||||
// all. Unittests show that the merging is inaccurate. Find out how to
|
||||
// do merges properly.
|
||||
var r float64
|
||||
i := 0
|
||||
for _, sample := range samples {
|
||||
for ; i < len(s.l); i++ {
|
||||
c := s.l[i]
|
||||
if c.Value > sample.Value {
|
||||
// Insert at position i.
|
||||
s.l = append(s.l, Sample{})
|
||||
copy(s.l[i+1:], s.l[i:])
|
||||
s.l[i] = Sample{
|
||||
sample.Value,
|
||||
sample.Width,
|
||||
math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1),
|
||||
// TODO(beorn7): How to calculate delta correctly?
|
||||
}
|
||||
i++
|
||||
goto inserted
|
||||
}
|
||||
r += c.Width
|
||||
}
|
||||
s.l = append(s.l, Sample{sample.Value, sample.Width, 0})
|
||||
i++
|
||||
inserted:
|
||||
s.n += sample.Width
|
||||
r += sample.Width
|
||||
}
|
||||
s.compress()
|
||||
}
|
||||
|
||||
func (s *stream) count() int {
|
||||
return int(s.n)
|
||||
}
|
||||
|
||||
func (s *stream) query(q float64) float64 {
|
||||
t := math.Ceil(q * s.n)
|
||||
t += math.Ceil(s.ƒ(s, t) / 2)
|
||||
p := s.l[0]
|
||||
var r float64
|
||||
for _, c := range s.l[1:] {
|
||||
r += p.Width
|
||||
if r+c.Width+c.Delta > t {
|
||||
return p.Value
|
||||
}
|
||||
p = c
|
||||
}
|
||||
return p.Value
|
||||
}
|
||||
|
||||
func (s *stream) compress() {
|
||||
if len(s.l) < 2 {
|
||||
return
|
||||
}
|
||||
x := s.l[len(s.l)-1]
|
||||
xi := len(s.l) - 1
|
||||
r := s.n - 1 - x.Width
|
||||
|
||||
for i := len(s.l) - 2; i >= 0; i-- {
|
||||
c := s.l[i]
|
||||
if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
|
||||
x.Width += c.Width
|
||||
s.l[xi] = x
|
||||
// Remove element at i.
|
||||
copy(s.l[i:], s.l[i+1:])
|
||||
s.l = s.l[:len(s.l)-1]
|
||||
xi -= 1
|
||||
} else {
|
||||
x = c
|
||||
xi = i
|
||||
}
|
||||
r -= c.Width
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stream) samples() Samples {
|
||||
samples := make(Samples, len(s.l))
|
||||
copy(samples, s.l)
|
||||
return samples
|
||||
}
|
||||
22
vendor/github.com/blang/semver/LICENSE
generated
vendored
Normal file
22
vendor/github.com/blang/semver/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2014 Benedikt Lang <github at benediktlang.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
142
vendor/github.com/blang/semver/README.md
generated
vendored
Normal file
142
vendor/github.com/blang/semver/README.md
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
semver for golang [](https://drone.io/github.com/blang/semver/latest) [](https://godoc.org/github.com/blang/semver) [](https://coveralls.io/r/blang/semver?branch=master)
|
||||
======
|
||||
|
||||
semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
|
||||
|
||||
Usage
|
||||
-----
|
||||
```bash
|
||||
$ go get github.com/blang/semver
|
||||
```
|
||||
Note: Always vendor your dependencies or fix on a specific version tag.
|
||||
|
||||
```go
|
||||
import github.com/blang/semver
|
||||
v1, err := semver.Make("1.0.0-beta")
|
||||
v2, err := semver.Make("2.0.0-beta")
|
||||
v1.Compare(v2)
|
||||
```
|
||||
|
||||
Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
|
||||
|
||||
Why should I use this lib?
|
||||
-----
|
||||
|
||||
- Fully spec compatible
|
||||
- No reflection
|
||||
- No regex
|
||||
- Fully tested (Coverage >99%)
|
||||
- Readable parsing/validation errors
|
||||
- Fast (See [Benchmarks](#benchmarks))
|
||||
- Only Stdlib
|
||||
- Uses values instead of pointers
|
||||
- Many features, see below
|
||||
|
||||
|
||||
Features
|
||||
-----
|
||||
|
||||
- Parsing and validation at all levels
|
||||
- Comparator-like comparisons
|
||||
- Compare Helper Methods
|
||||
- InPlace manipulation
|
||||
- Sortable (implements sort.Interface)
|
||||
- database/sql compatible (sql.Scanner/Valuer)
|
||||
- encoding/json compatible (json.Marshaler/Unmarshaler)
|
||||
|
||||
|
||||
Example
|
||||
-----
|
||||
|
||||
Have a look at full examples in [examples/main.go](examples/main.go)
|
||||
|
||||
```go
|
||||
import github.com/blang/semver
|
||||
|
||||
v, err := semver.Make("0.0.1-alpha.preview+123.github")
|
||||
fmt.Printf("Major: %d\n", v.Major)
|
||||
fmt.Printf("Minor: %d\n", v.Minor)
|
||||
fmt.Printf("Patch: %d\n", v.Patch)
|
||||
fmt.Printf("Pre: %s\n", v.Pre)
|
||||
fmt.Printf("Build: %s\n", v.Build)
|
||||
|
||||
// Prerelease versions array
|
||||
if len(v.Pre) > 0 {
|
||||
fmt.Println("Prerelease versions:")
|
||||
for i, pre := range v.Pre {
|
||||
fmt.Printf("%d: %q\n", i, pre)
|
||||
}
|
||||
}
|
||||
|
||||
// Build meta data array
|
||||
if len(v.Build) > 0 {
|
||||
fmt.Println("Build meta data:")
|
||||
for i, build := range v.Build {
|
||||
fmt.Printf("%d: %q\n", i, build)
|
||||
}
|
||||
}
|
||||
|
||||
v001, err := semver.Make("0.0.1")
|
||||
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
|
||||
v001.GT(v) == true
|
||||
v.LT(v001) == true
|
||||
v.GTE(v) == true
|
||||
v.LTE(v) == true
|
||||
|
||||
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
|
||||
v001.Compare(v) == 1
|
||||
v.Compare(v001) == -1
|
||||
v.Compare(v) == 0
|
||||
|
||||
// Manipulate Version in place:
|
||||
v.Pre[0], err = semver.NewPRVersion("beta")
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing pre release version: %q", err)
|
||||
}
|
||||
|
||||
fmt.Println("\nValidate versions:")
|
||||
v.Build[0] = "?"
|
||||
|
||||
err = v.Validate()
|
||||
if err != nil {
|
||||
fmt.Printf("Validation failed: %s\n", err)
|
||||
}
|
||||
```
|
||||
|
||||
Benchmarks
|
||||
-----
|
||||
|
||||
BenchmarkParseSimple 5000000 328 ns/op 49 B/op 1 allocs/op
|
||||
BenchmarkParseComplex 1000000 2105 ns/op 263 B/op 7 allocs/op
|
||||
BenchmarkParseAverage 1000000 1301 ns/op 168 B/op 4 allocs/op
|
||||
BenchmarkStringSimple 10000000 130 ns/op 5 B/op 1 allocs/op
|
||||
BenchmarkStringLarger 5000000 280 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkStringComplex 3000000 512 ns/op 80 B/op 3 allocs/op
|
||||
BenchmarkStringAverage 5000000 387 ns/op 47 B/op 2 allocs/op
|
||||
BenchmarkValidateSimple 500000000 7.92 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkValidateComplex 2000000 923 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkValidateAverage 5000000 452 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkCompareSimple 100000000 11.2 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkCompareComplex 50000000 40.9 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkCompareAverage 50000000 43.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSort 5000000 436 ns/op 259 B/op 2 allocs/op
|
||||
|
||||
See benchmark cases at [semver_test.go](semver_test.go)
|
||||
|
||||
|
||||
Motivation
|
||||
-----
|
||||
|
||||
I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
|
||||
|
||||
|
||||
Contribution
|
||||
-----
|
||||
|
||||
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
|
||||
|
||||
|
||||
License
|
||||
-----
|
||||
|
||||
See [LICENSE](LICENSE) file.
|
||||
23
vendor/github.com/blang/semver/json.go
generated
vendored
Normal file
23
vendor/github.com/blang/semver/json.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// MarshalJSON implements the encoding/json.Marshaler interface.
|
||||
func (v Version) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
|
||||
func (v *Version) UnmarshalJSON(data []byte) (err error) {
|
||||
var versionString string
|
||||
|
||||
if err = json.Unmarshal(data, &versionString); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
*v, err = Parse(versionString)
|
||||
|
||||
return
|
||||
}
|
||||
395
vendor/github.com/blang/semver/semver.go
generated
vendored
Normal file
395
vendor/github.com/blang/semver/semver.go
generated
vendored
Normal file
@@ -0,0 +1,395 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
numbers string = "0123456789"
|
||||
alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
|
||||
alphanum = alphas + numbers
|
||||
)
|
||||
|
||||
// SpecVersion is the latest fully supported spec version of semver
|
||||
var SpecVersion = Version{
|
||||
Major: 2,
|
||||
Minor: 0,
|
||||
Patch: 0,
|
||||
}
|
||||
|
||||
// Version represents a semver compatible version
|
||||
type Version struct {
|
||||
Major uint64
|
||||
Minor uint64
|
||||
Patch uint64
|
||||
Pre []PRVersion
|
||||
Build []string //No Precendence
|
||||
}
|
||||
|
||||
// Version to string
|
||||
func (v Version) String() string {
|
||||
b := make([]byte, 0, 5)
|
||||
b = strconv.AppendUint(b, v.Major, 10)
|
||||
b = append(b, '.')
|
||||
b = strconv.AppendUint(b, v.Minor, 10)
|
||||
b = append(b, '.')
|
||||
b = strconv.AppendUint(b, v.Patch, 10)
|
||||
|
||||
if len(v.Pre) > 0 {
|
||||
b = append(b, '-')
|
||||
b = append(b, v.Pre[0].String()...)
|
||||
|
||||
for _, pre := range v.Pre[1:] {
|
||||
b = append(b, '.')
|
||||
b = append(b, pre.String()...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(v.Build) > 0 {
|
||||
b = append(b, '+')
|
||||
b = append(b, v.Build[0]...)
|
||||
|
||||
for _, build := range v.Build[1:] {
|
||||
b = append(b, '.')
|
||||
b = append(b, build...)
|
||||
}
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Equals checks if v is equal to o.
|
||||
func (v Version) Equals(o Version) bool {
|
||||
return (v.Compare(o) == 0)
|
||||
}
|
||||
|
||||
// EQ checks if v is equal to o.
|
||||
func (v Version) EQ(o Version) bool {
|
||||
return (v.Compare(o) == 0)
|
||||
}
|
||||
|
||||
// NE checks if v is not equal to o.
|
||||
func (v Version) NE(o Version) bool {
|
||||
return (v.Compare(o) != 0)
|
||||
}
|
||||
|
||||
// GT checks if v is greater than o.
|
||||
func (v Version) GT(o Version) bool {
|
||||
return (v.Compare(o) == 1)
|
||||
}
|
||||
|
||||
// GTE checks if v is greater than or equal to o.
|
||||
func (v Version) GTE(o Version) bool {
|
||||
return (v.Compare(o) >= 0)
|
||||
}
|
||||
|
||||
// GE checks if v is greater than or equal to o.
|
||||
func (v Version) GE(o Version) bool {
|
||||
return (v.Compare(o) >= 0)
|
||||
}
|
||||
|
||||
// LT checks if v is less than o.
|
||||
func (v Version) LT(o Version) bool {
|
||||
return (v.Compare(o) == -1)
|
||||
}
|
||||
|
||||
// LTE checks if v is less than or equal to o.
|
||||
func (v Version) LTE(o Version) bool {
|
||||
return (v.Compare(o) <= 0)
|
||||
}
|
||||
|
||||
// LE checks if v is less than or equal to o.
|
||||
func (v Version) LE(o Version) bool {
|
||||
return (v.Compare(o) <= 0)
|
||||
}
|
||||
|
||||
// Compare compares Versions v to o:
|
||||
// -1 == v is less than o
|
||||
// 0 == v is equal to o
|
||||
// 1 == v is greater than o
|
||||
func (v Version) Compare(o Version) int {
|
||||
if v.Major != o.Major {
|
||||
if v.Major > o.Major {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
if v.Minor != o.Minor {
|
||||
if v.Minor > o.Minor {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
if v.Patch != o.Patch {
|
||||
if v.Patch > o.Patch {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Quick comparison if a version has no prerelease versions
|
||||
if len(v.Pre) == 0 && len(o.Pre) == 0 {
|
||||
return 0
|
||||
} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
|
||||
return 1
|
||||
} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
i := 0
|
||||
for ; i < len(v.Pre) && i < len(o.Pre); i++ {
|
||||
if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
|
||||
continue
|
||||
} else if comp == 1 {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// If all pr versions are the equal but one has further prversion, this one greater
|
||||
if i == len(v.Pre) && i == len(o.Pre) {
|
||||
return 0
|
||||
} else if i == len(v.Pre) && i < len(o.Pre) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Validate validates v and returns error in case
|
||||
func (v Version) Validate() error {
|
||||
// Major, Minor, Patch already validated using uint64
|
||||
|
||||
for _, pre := range v.Pre {
|
||||
if !pre.IsNum { //Numeric prerelease versions already uint64
|
||||
if len(pre.VersionStr) == 0 {
|
||||
return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
|
||||
}
|
||||
if !containsOnly(pre.VersionStr, alphanum) {
|
||||
return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, build := range v.Build {
|
||||
if len(build) == 0 {
|
||||
return fmt.Errorf("Build meta data can not be empty %q", build)
|
||||
}
|
||||
if !containsOnly(build, alphanum) {
|
||||
return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
|
||||
func New(s string) (vp *Version, err error) {
|
||||
v, err := Parse(s)
|
||||
vp = &v
|
||||
return
|
||||
}
|
||||
|
||||
// Make is an alias for Parse, parses version string and returns a validated Version or error
|
||||
func Make(s string) (Version, error) {
|
||||
return Parse(s)
|
||||
}
|
||||
|
||||
// Parse parses version string and returns a validated Version or error
|
||||
func Parse(s string) (Version, error) {
|
||||
if len(s) == 0 {
|
||||
return Version{}, errors.New("Version string empty")
|
||||
}
|
||||
|
||||
// Split into major.minor.(patch+pr+meta)
|
||||
parts := strings.SplitN(s, ".", 3)
|
||||
if len(parts) != 3 {
|
||||
return Version{}, errors.New("No Major.Minor.Patch elements found")
|
||||
}
|
||||
|
||||
// Major
|
||||
if !containsOnly(parts[0], numbers) {
|
||||
return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
|
||||
}
|
||||
if hasLeadingZeroes(parts[0]) {
|
||||
return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
|
||||
}
|
||||
major, err := strconv.ParseUint(parts[0], 10, 64)
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
|
||||
// Minor
|
||||
if !containsOnly(parts[1], numbers) {
|
||||
return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
|
||||
}
|
||||
if hasLeadingZeroes(parts[1]) {
|
||||
return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
|
||||
}
|
||||
minor, err := strconv.ParseUint(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
|
||||
v := Version{}
|
||||
v.Major = major
|
||||
v.Minor = minor
|
||||
|
||||
var build, prerelease []string
|
||||
patchStr := parts[2]
|
||||
|
||||
if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
|
||||
build = strings.Split(patchStr[buildIndex+1:], ".")
|
||||
patchStr = patchStr[:buildIndex]
|
||||
}
|
||||
|
||||
if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
|
||||
prerelease = strings.Split(patchStr[preIndex+1:], ".")
|
||||
patchStr = patchStr[:preIndex]
|
||||
}
|
||||
|
||||
if !containsOnly(patchStr, numbers) {
|
||||
return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
|
||||
}
|
||||
if hasLeadingZeroes(patchStr) {
|
||||
return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
|
||||
}
|
||||
patch, err := strconv.ParseUint(patchStr, 10, 64)
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
|
||||
v.Patch = patch
|
||||
|
||||
// Prerelease
|
||||
for _, prstr := range prerelease {
|
||||
parsedPR, err := NewPRVersion(prstr)
|
||||
if err != nil {
|
||||
return Version{}, err
|
||||
}
|
||||
v.Pre = append(v.Pre, parsedPR)
|
||||
}
|
||||
|
||||
// Build meta data
|
||||
for _, str := range build {
|
||||
if len(str) == 0 {
|
||||
return Version{}, errors.New("Build meta data is empty")
|
||||
}
|
||||
if !containsOnly(str, alphanum) {
|
||||
return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
|
||||
}
|
||||
v.Build = append(v.Build, str)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// MustParse is like Parse but panics if the version cannot be parsed.
|
||||
func MustParse(s string) Version {
|
||||
v, err := Parse(s)
|
||||
if err != nil {
|
||||
panic(`semver: Parse(` + s + `): ` + err.Error())
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// PRVersion represents a PreRelease Version
|
||||
type PRVersion struct {
|
||||
VersionStr string
|
||||
VersionNum uint64
|
||||
IsNum bool
|
||||
}
|
||||
|
||||
// NewPRVersion creates a new valid prerelease version
|
||||
func NewPRVersion(s string) (PRVersion, error) {
|
||||
if len(s) == 0 {
|
||||
return PRVersion{}, errors.New("Prerelease is empty")
|
||||
}
|
||||
v := PRVersion{}
|
||||
if containsOnly(s, numbers) {
|
||||
if hasLeadingZeroes(s) {
|
||||
return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
|
||||
}
|
||||
num, err := strconv.ParseUint(s, 10, 64)
|
||||
|
||||
// Might never be hit, but just in case
|
||||
if err != nil {
|
||||
return PRVersion{}, err
|
||||
}
|
||||
v.VersionNum = num
|
||||
v.IsNum = true
|
||||
} else if containsOnly(s, alphanum) {
|
||||
v.VersionStr = s
|
||||
v.IsNum = false
|
||||
} else {
|
||||
return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// IsNumeric checks if prerelease-version is numeric
|
||||
func (v PRVersion) IsNumeric() bool {
|
||||
return v.IsNum
|
||||
}
|
||||
|
||||
// Compare compares two PreRelease Versions v and o:
|
||||
// -1 == v is less than o
|
||||
// 0 == v is equal to o
|
||||
// 1 == v is greater than o
|
||||
func (v PRVersion) Compare(o PRVersion) int {
|
||||
if v.IsNum && !o.IsNum {
|
||||
return -1
|
||||
} else if !v.IsNum && o.IsNum {
|
||||
return 1
|
||||
} else if v.IsNum && o.IsNum {
|
||||
if v.VersionNum == o.VersionNum {
|
||||
return 0
|
||||
} else if v.VersionNum > o.VersionNum {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
} else { // both are Alphas
|
||||
if v.VersionStr == o.VersionStr {
|
||||
return 0
|
||||
} else if v.VersionStr > o.VersionStr {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PreRelease version to string
|
||||
func (v PRVersion) String() string {
|
||||
if v.IsNum {
|
||||
return strconv.FormatUint(v.VersionNum, 10)
|
||||
}
|
||||
return v.VersionStr
|
||||
}
|
||||
|
||||
func containsOnly(s string, set string) bool {
|
||||
return strings.IndexFunc(s, func(r rune) bool {
|
||||
return !strings.ContainsRune(set, r)
|
||||
}) == -1
|
||||
}
|
||||
|
||||
func hasLeadingZeroes(s string) bool {
|
||||
return len(s) > 1 && s[0] == '0'
|
||||
}
|
||||
|
||||
// NewBuildVersion creates a new valid build version
|
||||
func NewBuildVersion(s string) (string, error) {
|
||||
if len(s) == 0 {
|
||||
return "", errors.New("Buildversion is empty")
|
||||
}
|
||||
if !containsOnly(s, alphanum) {
|
||||
return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
28
vendor/github.com/blang/semver/sort.go
generated
vendored
Normal file
28
vendor/github.com/blang/semver/sort.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Versions represents multiple versions.
|
||||
type Versions []Version
|
||||
|
||||
// Len returns length of version collection
|
||||
func (s Versions) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// Swap swaps two versions inside the collection by its indices
|
||||
func (s Versions) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Less checks if version at index i is less than version at index j
|
||||
func (s Versions) Less(i, j int) bool {
|
||||
return s[i].LT(s[j])
|
||||
}
|
||||
|
||||
// Sort sorts a slice of versions
|
||||
func Sort(versions []Version) {
|
||||
sort.Sort(Versions(versions))
|
||||
}
|
||||
30
vendor/github.com/blang/semver/sql.go
generated
vendored
Normal file
30
vendor/github.com/blang/semver/sql.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Scan implements the database/sql.Scanner interface.
|
||||
func (v *Version) Scan(src interface{}) (err error) {
|
||||
var str string
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
str = src
|
||||
case []byte:
|
||||
str = string(src)
|
||||
default:
|
||||
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
|
||||
}
|
||||
|
||||
if t, err := Parse(str); err == nil {
|
||||
*v = t
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver.Valuer interface.
|
||||
func (v Version) Value() (driver.Value, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
13
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
13
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright (c) 2012-2013 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
151
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
Normal file
151
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is not running on Google App Engine and "-tags disableunsafe"
|
||||
// is not added to the go build command line.
|
||||
// +build !appengine,!disableunsafe
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = false
|
||||
|
||||
// ptrSize is the size of a pointer on the current arch.
|
||||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
)
|
||||
|
||||
var (
|
||||
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
||||
// internal reflect.Value fields. These values are valid before golang
|
||||
// commit ecccf07e7f9d which changed the format. The are also valid
|
||||
// after commit 82f48826c6c7 which changed the format again to mirror
|
||||
// the original format. Code in the init function updates these offsets
|
||||
// as necessary.
|
||||
offsetPtr = uintptr(ptrSize)
|
||||
offsetScalar = uintptr(0)
|
||||
offsetFlag = uintptr(ptrSize * 2)
|
||||
|
||||
// flagKindWidth and flagKindShift indicate various bits that the
|
||||
// reflect package uses internally to track kind information.
|
||||
//
|
||||
// flagRO indicates whether or not the value field of a reflect.Value is
|
||||
// read-only.
|
||||
//
|
||||
// flagIndir indicates whether the value field of a reflect.Value is
|
||||
// the actual data or a pointer to the data.
|
||||
//
|
||||
// These values are valid before golang commit 90a7c3c86944 which
|
||||
// changed their positions. Code in the init function updates these
|
||||
// flags as necessary.
|
||||
flagKindWidth = uintptr(5)
|
||||
flagKindShift = uintptr(flagKindWidth - 1)
|
||||
flagRO = uintptr(1 << 0)
|
||||
flagIndir = uintptr(1 << 1)
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Older versions of reflect.Value stored small integers directly in the
|
||||
// ptr field (which is named val in the older versions). Versions
|
||||
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
||||
// scalar for this purpose which unfortunately came before the flag
|
||||
// field, so the offset of the flag field is different for those
|
||||
// versions.
|
||||
//
|
||||
// This code constructs a new reflect.Value from a known small integer
|
||||
// and checks if the size of the reflect.Value struct indicates it has
|
||||
// the scalar field. When it does, the offsets are updated accordingly.
|
||||
vv := reflect.ValueOf(0xf00)
|
||||
if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
||||
offsetScalar = ptrSize * 2
|
||||
offsetFlag = ptrSize * 3
|
||||
}
|
||||
|
||||
// Commit 90a7c3c86944 changed the flag positions such that the low
|
||||
// order bits are the kind. This code extracts the kind from the flags
|
||||
// field and ensures it's the correct type. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are updated
|
||||
// accordingly.
|
||||
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
|
||||
upfv := *(*uintptr)(upf)
|
||||
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
|
||||
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
||||
flagKindShift = 0
|
||||
flagRO = 1 << 5
|
||||
flagIndir = 1 << 6
|
||||
|
||||
// Commit adf9b30e5594 modified the flags to separate the
|
||||
// flagRO flag into two bits which specifies whether or not the
|
||||
// field is embedded. This causes flagIndir to move over a bit
|
||||
// and means that flagRO is the combination of either of the
|
||||
// original flagRO bit and the new bit.
|
||||
//
|
||||
// This code detects the change by extracting what used to be
|
||||
// the indirect bit to ensure it's set. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are
|
||||
// updated accordingly.
|
||||
if upfv&flagIndir == 0 {
|
||||
flagRO = 3 << 5
|
||||
flagIndir = 1 << 7
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||
// the typical safety restrictions preventing access to unaddressable and
|
||||
// unexported data. It works by digging the raw pointer to the underlying
|
||||
// value out of the protected value and generating a new unprotected (unsafe)
|
||||
// reflect.Value to it.
|
||||
//
|
||||
// This allows us to check for implementations of the Stringer and error
|
||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||
// inaccessible values such as unexported struct fields.
|
||||
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
||||
indirects := 1
|
||||
vt := v.Type()
|
||||
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
|
||||
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
|
||||
if rvf&flagIndir != 0 {
|
||||
vt = reflect.PtrTo(v.Type())
|
||||
indirects++
|
||||
} else if offsetScalar != 0 {
|
||||
// The value is in the scalar field when it's not one of the
|
||||
// reference types.
|
||||
switch vt.Kind() {
|
||||
case reflect.Uintptr:
|
||||
case reflect.Chan:
|
||||
case reflect.Func:
|
||||
case reflect.Map:
|
||||
case reflect.Ptr:
|
||||
case reflect.UnsafePointer:
|
||||
default:
|
||||
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
|
||||
offsetScalar)
|
||||
}
|
||||
}
|
||||
|
||||
pv := reflect.NewAt(vt, upv)
|
||||
rv = pv
|
||||
for i := 0; i < indirects; i++ {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
return rv
|
||||
}
|
||||
37
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
Normal file
37
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when either the code is running on Google App Engine or "-tags disableunsafe"
|
||||
// is added to the go build command line.
|
||||
// +build appengine disableunsafe
|
||||
|
||||
package spew
|
||||
|
||||
import "reflect"
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = true
|
||||
)
|
||||
|
||||
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
||||
// that bypasses the typical safety restrictions preventing access to
|
||||
// unaddressable and unexported data. However, doing this relies on access to
|
||||
// the unsafe package. This is a stub version which simply returns the passed
|
||||
// reflect.Value when the unsafe package is not available.
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
return v
|
||||
}
|
||||
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
Normal file
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Some constants in the form of bytes to avoid string overhead. This mirrors
|
||||
// the technique used in the fmt package.
|
||||
var (
|
||||
panicBytes = []byte("(PANIC=")
|
||||
plusBytes = []byte("+")
|
||||
iBytes = []byte("i")
|
||||
trueBytes = []byte("true")
|
||||
falseBytes = []byte("false")
|
||||
interfaceBytes = []byte("(interface {})")
|
||||
commaNewlineBytes = []byte(",\n")
|
||||
newlineBytes = []byte("\n")
|
||||
openBraceBytes = []byte("{")
|
||||
openBraceNewlineBytes = []byte("{\n")
|
||||
closeBraceBytes = []byte("}")
|
||||
asteriskBytes = []byte("*")
|
||||
colonBytes = []byte(":")
|
||||
colonSpaceBytes = []byte(": ")
|
||||
openParenBytes = []byte("(")
|
||||
closeParenBytes = []byte(")")
|
||||
spaceBytes = []byte(" ")
|
||||
pointerChainBytes = []byte("->")
|
||||
nilAngleBytes = []byte("<nil>")
|
||||
maxNewlineBytes = []byte("<max depth reached>\n")
|
||||
maxShortBytes = []byte("<max>")
|
||||
circularBytes = []byte("<already shown>")
|
||||
circularShortBytes = []byte("<shown>")
|
||||
invalidAngleBytes = []byte("<invalid>")
|
||||
openBracketBytes = []byte("[")
|
||||
closeBracketBytes = []byte("]")
|
||||
percentBytes = []byte("%")
|
||||
precisionBytes = []byte(".")
|
||||
openAngleBytes = []byte("<")
|
||||
closeAngleBytes = []byte(">")
|
||||
openMapBytes = []byte("map[")
|
||||
closeMapBytes = []byte("]")
|
||||
lenEqualsBytes = []byte("len=")
|
||||
capEqualsBytes = []byte("cap=")
|
||||
)
|
||||
|
||||
// hexDigits is used to map a decimal value to a hex digit.
|
||||
var hexDigits = "0123456789abcdef"
|
||||
|
||||
// catchPanic handles any panics that might occur during the handleMethods
|
||||
// calls.
|
||||
func catchPanic(w io.Writer, v reflect.Value) {
|
||||
if err := recover(); err != nil {
|
||||
w.Write(panicBytes)
|
||||
fmt.Fprintf(w, "%v", err)
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// handleMethods attempts to call the Error and String methods on the underlying
|
||||
// type the passed reflect.Value represents and outputes the result to Writer w.
|
||||
//
|
||||
// It handles panics in any called methods by catching and displaying the error
|
||||
// as the formatted value.
|
||||
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
|
||||
// We need an interface to check if the type implements the error or
|
||||
// Stringer interface. However, the reflect package won't give us an
|
||||
// interface on certain things like unexported struct fields in order
|
||||
// to enforce visibility rules. We use unsafe, when it's available,
|
||||
// to bypass these restrictions since this package does not mutate the
|
||||
// values.
|
||||
if !v.CanInterface() {
|
||||
if UnsafeDisabled {
|
||||
return false
|
||||
}
|
||||
|
||||
v = unsafeReflectValue(v)
|
||||
}
|
||||
|
||||
// Choose whether or not to do error and Stringer interface lookups against
|
||||
// the base type or a pointer to the base type depending on settings.
|
||||
// Technically calling one of these methods with a pointer receiver can
|
||||
// mutate the value, however, types which choose to satisify an error or
|
||||
// Stringer interface with a pointer receiver should not be mutating their
|
||||
// state inside these interface methods.
|
||||
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
|
||||
v = unsafeReflectValue(v)
|
||||
}
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
|
||||
// Is it an error or Stringer?
|
||||
switch iface := v.Interface().(type) {
|
||||
case error:
|
||||
defer catchPanic(w, v)
|
||||
if cs.ContinueOnMethod {
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(iface.Error()))
|
||||
w.Write(closeParenBytes)
|
||||
w.Write(spaceBytes)
|
||||
return false
|
||||
}
|
||||
|
||||
w.Write([]byte(iface.Error()))
|
||||
return true
|
||||
|
||||
case fmt.Stringer:
|
||||
defer catchPanic(w, v)
|
||||
if cs.ContinueOnMethod {
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(iface.String()))
|
||||
w.Write(closeParenBytes)
|
||||
w.Write(spaceBytes)
|
||||
return false
|
||||
}
|
||||
w.Write([]byte(iface.String()))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// printBool outputs a boolean value as true or false to Writer w.
|
||||
func printBool(w io.Writer, val bool) {
|
||||
if val {
|
||||
w.Write(trueBytes)
|
||||
} else {
|
||||
w.Write(falseBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// printInt outputs a signed integer value to Writer w.
|
||||
func printInt(w io.Writer, val int64, base int) {
|
||||
w.Write([]byte(strconv.FormatInt(val, base)))
|
||||
}
|
||||
|
||||
// printUint outputs an unsigned integer value to Writer w.
|
||||
func printUint(w io.Writer, val uint64, base int) {
|
||||
w.Write([]byte(strconv.FormatUint(val, base)))
|
||||
}
|
||||
|
||||
// printFloat outputs a floating point value using the specified precision,
|
||||
// which is expected to be 32 or 64bit, to Writer w.
|
||||
func printFloat(w io.Writer, val float64, precision int) {
|
||||
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
|
||||
}
|
||||
|
||||
// printComplex outputs a complex value using the specified float precision
|
||||
// for the real and imaginary parts to Writer w.
|
||||
func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
||||
r := real(c)
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
|
||||
i := imag(c)
|
||||
if i >= 0 {
|
||||
w.Write(plusBytes)
|
||||
}
|
||||
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
|
||||
w.Write(iBytes)
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
|
||||
// prefix to Writer w.
|
||||
func printHexPtr(w io.Writer, p uintptr) {
|
||||
// Null pointer.
|
||||
num := uint64(p)
|
||||
if num == 0 {
|
||||
w.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
|
||||
buf := make([]byte, 18)
|
||||
|
||||
// It's simpler to construct the hex string right to left.
|
||||
base := uint64(16)
|
||||
i := len(buf) - 1
|
||||
for num >= base {
|
||||
buf[i] = hexDigits[num%base]
|
||||
num /= base
|
||||
i--
|
||||
}
|
||||
buf[i] = hexDigits[num]
|
||||
|
||||
// Add '0x' prefix.
|
||||
i--
|
||||
buf[i] = 'x'
|
||||
i--
|
||||
buf[i] = '0'
|
||||
|
||||
// Strip unused leading bytes.
|
||||
buf = buf[i:]
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
|
||||
// elements to be sorted.
|
||||
type valuesSorter struct {
|
||||
values []reflect.Value
|
||||
strings []string // either nil or same len and values
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// newValuesSorter initializes a valuesSorter instance, which holds a set of
|
||||
// surrogate keys on which the data should be sorted. It uses flags in
|
||||
// ConfigState to decide if and how to populate those surrogate keys.
|
||||
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
|
||||
vs := &valuesSorter{values: values, cs: cs}
|
||||
if canSortSimply(vs.values[0].Kind()) {
|
||||
return vs
|
||||
}
|
||||
if !cs.DisableMethods {
|
||||
vs.strings = make([]string, len(values))
|
||||
for i := range vs.values {
|
||||
b := bytes.Buffer{}
|
||||
if !handleMethods(cs, &b, vs.values[i]) {
|
||||
vs.strings = nil
|
||||
break
|
||||
}
|
||||
vs.strings[i] = b.String()
|
||||
}
|
||||
}
|
||||
if vs.strings == nil && cs.SpewKeys {
|
||||
vs.strings = make([]string, len(values))
|
||||
for i := range vs.values {
|
||||
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
|
||||
}
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
|
||||
// directly, or whether it should be considered for sorting by surrogate keys
|
||||
// (if the ConfigState allows it).
|
||||
func canSortSimply(kind reflect.Kind) bool {
|
||||
// This switch parallels valueSortLess, except for the default case.
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
return true
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return true
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
return true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return true
|
||||
case reflect.String:
|
||||
return true
|
||||
case reflect.Uintptr:
|
||||
return true
|
||||
case reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Len returns the number of values in the slice. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s *valuesSorter) Len() int {
|
||||
return len(s.values)
|
||||
}
|
||||
|
||||
// Swap swaps the values at the passed indices. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s *valuesSorter) Swap(i, j int) {
|
||||
s.values[i], s.values[j] = s.values[j], s.values[i]
|
||||
if s.strings != nil {
|
||||
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
|
||||
}
|
||||
}
|
||||
|
||||
// valueSortLess returns whether the first value should sort before the second
|
||||
// value. It is used by valueSorter.Less as part of the sort.Interface
|
||||
// implementation.
|
||||
func valueSortLess(a, b reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Bool:
|
||||
return !a.Bool() && b.Bool()
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return a.Int() < b.Int()
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return a.Float() < b.Float()
|
||||
case reflect.String:
|
||||
return a.String() < b.String()
|
||||
case reflect.Uintptr:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Array:
|
||||
// Compare the contents of both arrays.
|
||||
l := a.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
av := a.Index(i)
|
||||
bv := b.Index(i)
|
||||
if av.Interface() == bv.Interface() {
|
||||
continue
|
||||
}
|
||||
return valueSortLess(av, bv)
|
||||
}
|
||||
}
|
||||
return a.String() < b.String()
|
||||
}
|
||||
|
||||
// Less returns whether the value at index i should sort before the
|
||||
// value at index j. It is part of the sort.Interface implementation.
|
||||
func (s *valuesSorter) Less(i, j int) bool {
|
||||
if s.strings == nil {
|
||||
return valueSortLess(s.values[i], s.values[j])
|
||||
}
|
||||
return s.strings[i] < s.strings[j]
|
||||
}
|
||||
|
||||
// sortValues is a sort function that handles both native types and any type that
|
||||
// can be converted to error or Stringer. Other inputs are sorted according to
|
||||
// their Value.String() value to ensure display stability.
|
||||
func sortValues(values []reflect.Value, cs *ConfigState) {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
sort.Sort(newValuesSorter(values, cs))
|
||||
}
|
||||
297
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
Normal file
297
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ConfigState houses the configuration options used by spew to format and
|
||||
// display values. There is a global instance, Config, that is used to control
|
||||
// all top-level Formatter and Dump functionality. Each ConfigState instance
|
||||
// provides methods equivalent to the top-level functions.
|
||||
//
|
||||
// The zero value for ConfigState provides no indentation. You would typically
|
||||
// want to set it to a space or a tab.
|
||||
//
|
||||
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
|
||||
// with default settings. See the documentation of NewDefaultConfig for default
|
||||
// values.
|
||||
type ConfigState struct {
|
||||
// Indent specifies the string to use for each indentation level. The
|
||||
// global config instance that all top-level functions use set this to a
|
||||
// single space by default. If you would like more indentation, you might
|
||||
// set this to a tab with "\t" or perhaps two spaces with " ".
|
||||
Indent string
|
||||
|
||||
// MaxDepth controls the maximum number of levels to descend into nested
|
||||
// data structures. The default, 0, means there is no limit.
|
||||
//
|
||||
// NOTE: Circular data structures are properly detected, so it is not
|
||||
// necessary to set this value unless you specifically want to limit deeply
|
||||
// nested data structures.
|
||||
MaxDepth int
|
||||
|
||||
// DisableMethods specifies whether or not error and Stringer interfaces are
|
||||
// invoked for types that implement them.
|
||||
DisableMethods bool
|
||||
|
||||
// DisablePointerMethods specifies whether or not to check for and invoke
|
||||
// error and Stringer interfaces on types which only accept a pointer
|
||||
// receiver when the current type is not a pointer.
|
||||
//
|
||||
// NOTE: This might be an unsafe action since calling one of these methods
|
||||
// with a pointer receiver could technically mutate the value, however,
|
||||
// in practice, types which choose to satisify an error or Stringer
|
||||
// interface with a pointer receiver should not be mutating their state
|
||||
// inside these interface methods. As a result, this option relies on
|
||||
// access to the unsafe package, so it will not have any effect when
|
||||
// running in environments without access to the unsafe package such as
|
||||
// Google App Engine or with the "disableunsafe" build tag specified.
|
||||
DisablePointerMethods bool
|
||||
|
||||
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||
// a custom error or Stringer interface is invoked. The default, false,
|
||||
// means it will print the results of invoking the custom error or Stringer
|
||||
// interface and return immediately instead of continuing to recurse into
|
||||
// the internals of the data type.
|
||||
//
|
||||
// NOTE: This flag does not have any effect if method invocation is disabled
|
||||
// via the DisableMethods or DisablePointerMethods options.
|
||||
ContinueOnMethod bool
|
||||
|
||||
// SortKeys specifies map keys should be sorted before being printed. Use
|
||||
// this to have a more deterministic, diffable output. Note that only
|
||||
// native types (bool, int, uint, floats, uintptr and string) and types
|
||||
// that support the error or Stringer interfaces (if methods are
|
||||
// enabled) are supported, with other types sorted according to the
|
||||
// reflect.Value.String() output which guarantees display stability.
|
||||
SortKeys bool
|
||||
|
||||
// SpewKeys specifies that, as a last resort attempt, map keys should
|
||||
// be spewed to strings and sorted by those strings. This is only
|
||||
// considered if SortKeys is true.
|
||||
SpewKeys bool
|
||||
}
|
||||
|
||||
// Config is the active configuration of the top-level functions.
|
||||
// The configuration can be changed by modifying the contents of spew.Config.
|
||||
var Config = ConfigState{Indent: " "}
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the formatted string as a value that satisfies error. See NewFormatter
|
||||
// for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
|
||||
return fmt.Errorf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(w, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(w, format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||
// passed with a Formatter interface returned by c.NewFormatter. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Print(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Printf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Println(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprint(a ...interface{}) string {
|
||||
return fmt.Sprint(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||
// were passed with a Formatter interface returned by c.NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprintln(a ...interface{}) string {
|
||||
return fmt.Sprintln(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
printing functions. The formatter is useful for inline printing of smaller data
|
||||
types similar to the standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Typically this function shouldn't be called directly. It is much easier to make
|
||||
use of the custom formatter by calling one of the convenience functions such as
|
||||
c.Printf, c.Println, or c.Printf.
|
||||
*/
|
||||
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
|
||||
return newFormatter(c, v)
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
|
||||
fdump(c, w, a...)
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by modifying the public members
|
||||
of c. See ConfigState for options documentation.
|
||||
|
||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||
get the formatted result as a string.
|
||||
*/
|
||||
func (c *ConfigState) Dump(a ...interface{}) {
|
||||
fdump(c, os.Stdout, a...)
|
||||
}
|
||||
|
||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||
// as Dump.
|
||||
func (c *ConfigState) Sdump(a ...interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
fdump(c, &buf, a...)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||
// length with each argument converted to a spew Formatter interface using
|
||||
// the ConfigState associated with s.
|
||||
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
|
||||
formatters = make([]interface{}, len(args))
|
||||
for index, arg := range args {
|
||||
formatters[index] = newFormatter(c, arg)
|
||||
}
|
||||
return formatters
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a ConfigState with the following default settings.
|
||||
//
|
||||
// Indent: " "
|
||||
// MaxDepth: 0
|
||||
// DisableMethods: false
|
||||
// DisablePointerMethods: false
|
||||
// ContinueOnMethod: false
|
||||
// SortKeys: false
|
||||
func NewDefaultConfig() *ConfigState {
|
||||
return &ConfigState{Indent: " "}
|
||||
}
|
||||
202
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
Normal file
202
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package spew implements a deep pretty printer for Go data structures to aid in
|
||||
debugging.
|
||||
|
||||
A quick overview of the additional features spew provides over the built-in
|
||||
printing facilities for Go data types are as follows:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output (only when using
|
||||
Dump style)
|
||||
|
||||
There are two different approaches spew allows for dumping Go data structures:
|
||||
|
||||
* Dump style which prints with newlines, customizable indentation,
|
||||
and additional debug information such as types and all pointer addresses
|
||||
used to indirect to the final value
|
||||
* A custom Formatter interface that integrates cleanly with the standard fmt
|
||||
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
|
||||
similar to the default %v while providing the additional functionality
|
||||
outlined above and passing unsupported format verbs such as %x and %q
|
||||
along to fmt
|
||||
|
||||
Quick Start
|
||||
|
||||
This section demonstrates how to quickly get started with spew. See the
|
||||
sections below for further details on formatting and configuration options.
|
||||
|
||||
To dump a variable with full newlines, indentation, type, and pointer
|
||||
information use Dump, Fdump, or Sdump:
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
|
||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||
printing style, use the convenience wrappers Printf, Fprintf, etc with
|
||||
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
|
||||
%#+v (adds types and pointer addresses):
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
|
||||
Configuration Options
|
||||
|
||||
Configuration of spew is handled by fields in the ConfigState type. For
|
||||
convenience, all of the top-level functions use a global state available
|
||||
via the spew.Config global.
|
||||
|
||||
It is also possible to create a ConfigState instance that provides methods
|
||||
equivalent to the top-level functions. This allows concurrent configuration
|
||||
options. See the ConfigState documentation for more details.
|
||||
|
||||
The following configuration options are available:
|
||||
* Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
|
||||
* MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
|
||||
* DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
|
||||
* DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
* SortKeys
|
||||
Specifies map keys should be sorted before being printed. Use
|
||||
this to have a more deterministic, diffable output. Note that
|
||||
only native types (bool, int, uint, floats, uintptr and string)
|
||||
and types which implement error or Stringer interfaces are
|
||||
supported with other types sorted according to the
|
||||
reflect.Value.String() output which guarantees display
|
||||
stability. Natural map order is used by default.
|
||||
|
||||
* SpewKeys
|
||||
Specifies that, as a last resort attempt, map keys should be
|
||||
spewed to strings and sorted by those strings. This is only
|
||||
considered if SortKeys is true.
|
||||
|
||||
Dump Usage
|
||||
|
||||
Simply call spew.Dump with a list of variables you want to dump:
|
||||
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
|
||||
You may also call spew.Fdump if you would prefer to output to an arbitrary
|
||||
io.Writer. For example, to dump to standard error:
|
||||
|
||||
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
|
||||
|
||||
A third option is to call spew.Sdump to get the formatted output as a string:
|
||||
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
|
||||
Sample Dump Output
|
||||
|
||||
See the Dump example for details on the setup of the types and variables being
|
||||
shown here.
|
||||
|
||||
(main.Foo) {
|
||||
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||
flag: (main.Flag) flagTwo,
|
||||
data: (uintptr) <nil>
|
||||
}),
|
||||
ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
(string) (len=3) "one": (bool) true
|
||||
}
|
||||
}
|
||||
|
||||
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
|
||||
command as shown.
|
||||
([]uint8) (len=32 cap=32) {
|
||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
00000020 31 32 |12|
|
||||
}
|
||||
|
||||
Custom Formatter
|
||||
|
||||
Spew provides a custom formatter that implements the fmt.Formatter interface
|
||||
so that it integrates cleanly with standard fmt package printing functions. The
|
||||
formatter is useful for inline printing of smaller data types similar to the
|
||||
standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Custom Formatter Usage
|
||||
|
||||
The simplest way to make use of the spew custom formatter is to call one of the
|
||||
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
||||
functions have syntax you are most likely already familiar with:
|
||||
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Println(myVar, myVar2)
|
||||
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
|
||||
See the Index for the full list convenience functions.
|
||||
|
||||
Sample Formatter Output
|
||||
|
||||
Double pointer to a uint8:
|
||||
%v: <**>5
|
||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||
%#v: (**uint8)5
|
||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||
|
||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||
%v: <*>{1 <*><shown>}
|
||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||
|
||||
See the Printf example for details on the setup of variables being shown
|
||||
here.
|
||||
|
||||
Errors
|
||||
|
||||
Since it is possible for custom Stringer/error interfaces to panic, spew
|
||||
detects them and handles them internally by printing the panic information
|
||||
inline with the output. Since spew is intended to provide deep pretty printing
|
||||
capabilities on structures, it intentionally does not return any errors.
|
||||
*/
|
||||
package spew
|
||||
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
Normal file
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
Normal file
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// uint8Type is a reflect.Type representing a uint8. It is used to
|
||||
// convert cgo types to uint8 slices for hexdumping.
|
||||
uint8Type = reflect.TypeOf(uint8(0))
|
||||
|
||||
// cCharRE is a regular expression that matches a cgo char.
|
||||
// It is used to detect character arrays to hexdump them.
|
||||
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
||||
|
||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||
// char. It is used to detect unsigned character arrays to hexdump
|
||||
// them.
|
||||
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
||||
|
||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||
// It is used to detect uint8_t arrays to hexdump them.
|
||||
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
||||
)
|
||||
|
||||
// dumpState contains information about the state of a dump operation.
|
||||
type dumpState struct {
|
||||
w io.Writer
|
||||
depth int
|
||||
pointers map[uintptr]int
|
||||
ignoreNextType bool
|
||||
ignoreNextIndent bool
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// indent performs indentation according to the depth level and cs.Indent
|
||||
// option.
|
||||
func (d *dumpState) indent() {
|
||||
if d.ignoreNextIndent {
|
||||
d.ignoreNextIndent = false
|
||||
return
|
||||
}
|
||||
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
||||
}
|
||||
|
||||
// unpackValue returns values inside of non-nil interfaces when possible.
|
||||
// This is useful for data types like structs, arrays, slices, and maps which
|
||||
// can contain varying types packed inside an interface.
|
||||
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
||||
func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||
// Remove pointers at or below the current depth from map used to detect
|
||||
// circular refs.
|
||||
for k, depth := range d.pointers {
|
||||
if depth >= d.depth {
|
||||
delete(d.pointers, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep list of all dereferenced pointers to show later.
|
||||
pointerChain := make([]uintptr, 0)
|
||||
|
||||
// Figure out how many levels of indirection there are by dereferencing
|
||||
// pointers and unpacking interfaces down the chain while detecting circular
|
||||
// references.
|
||||
nilFound := false
|
||||
cycleFound := false
|
||||
indirects := 0
|
||||
ve := v
|
||||
for ve.Kind() == reflect.Ptr {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
indirects++
|
||||
addr := ve.Pointer()
|
||||
pointerChain = append(pointerChain, addr)
|
||||
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
|
||||
cycleFound = true
|
||||
indirects--
|
||||
break
|
||||
}
|
||||
d.pointers[addr] = d.depth
|
||||
|
||||
ve = ve.Elem()
|
||||
if ve.Kind() == reflect.Interface {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
ve = ve.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// Display type information.
|
||||
d.w.Write(openParenBytes)
|
||||
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||
d.w.Write([]byte(ve.Type().String()))
|
||||
d.w.Write(closeParenBytes)
|
||||
|
||||
// Display pointer information.
|
||||
if len(pointerChain) > 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
d.w.Write(pointerChainBytes)
|
||||
}
|
||||
printHexPtr(d.w, addr)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// Display dereferenced value.
|
||||
d.w.Write(openParenBytes)
|
||||
switch {
|
||||
case nilFound == true:
|
||||
d.w.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
d.w.Write(circularBytes)
|
||||
|
||||
default:
|
||||
d.ignoreNextType = true
|
||||
d.dump(ve)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
||||
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
||||
func (d *dumpState) dumpSlice(v reflect.Value) {
|
||||
// Determine whether this type should be hex dumped or not. Also,
|
||||
// for types which should be hexdumped, try to use the underlying data
|
||||
// first, then fall back to trying to convert them to a uint8 slice.
|
||||
var buf []uint8
|
||||
doConvert := false
|
||||
doHexDump := false
|
||||
numEntries := v.Len()
|
||||
if numEntries > 0 {
|
||||
vt := v.Index(0).Type()
|
||||
vts := vt.String()
|
||||
switch {
|
||||
// C types that need to be converted.
|
||||
case cCharRE.MatchString(vts):
|
||||
fallthrough
|
||||
case cUnsignedCharRE.MatchString(vts):
|
||||
fallthrough
|
||||
case cUint8tCharRE.MatchString(vts):
|
||||
doConvert = true
|
||||
|
||||
// Try to use existing uint8 slices and fall back to converting
|
||||
// and copying if that fails.
|
||||
case vt.Kind() == reflect.Uint8:
|
||||
// We need an addressable interface to convert the type
|
||||
// to a byte slice. However, the reflect package won't
|
||||
// give us an interface on certain things like
|
||||
// unexported struct fields in order to enforce
|
||||
// visibility rules. We use unsafe, when available, to
|
||||
// bypass these restrictions since this package does not
|
||||
// mutate the values.
|
||||
vs := v
|
||||
if !vs.CanInterface() || !vs.CanAddr() {
|
||||
vs = unsafeReflectValue(vs)
|
||||
}
|
||||
if !UnsafeDisabled {
|
||||
vs = vs.Slice(0, numEntries)
|
||||
|
||||
// Use the existing uint8 slice if it can be
|
||||
// type asserted.
|
||||
iface := vs.Interface()
|
||||
if slice, ok := iface.([]uint8); ok {
|
||||
buf = slice
|
||||
doHexDump = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// The underlying data needs to be converted if it can't
|
||||
// be type asserted to a uint8 slice.
|
||||
doConvert = true
|
||||
}
|
||||
|
||||
// Copy and convert the underlying type if needed.
|
||||
if doConvert && vt.ConvertibleTo(uint8Type) {
|
||||
// Convert and copy each element into a uint8 byte
|
||||
// slice.
|
||||
buf = make([]uint8, numEntries)
|
||||
for i := 0; i < numEntries; i++ {
|
||||
vv := v.Index(i)
|
||||
buf[i] = uint8(vv.Convert(uint8Type).Uint())
|
||||
}
|
||||
doHexDump = true
|
||||
}
|
||||
}
|
||||
|
||||
// Hexdump the entire slice as needed.
|
||||
if doHexDump {
|
||||
indent := strings.Repeat(d.cs.Indent, d.depth)
|
||||
str := indent + hex.Dump(buf)
|
||||
str = strings.Replace(str, "\n", "\n"+indent, -1)
|
||||
str = strings.TrimRight(str, d.cs.Indent)
|
||||
d.w.Write([]byte(str))
|
||||
return
|
||||
}
|
||||
|
||||
// Recursively call dump for each item.
|
||||
for i := 0; i < numEntries; i++ {
|
||||
d.dump(d.unpackValue(v.Index(i)))
|
||||
if i < (numEntries - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
||||
// value to figure out what kind of object we are dealing with and formats it
|
||||
// appropriately. It is a recursive function, however circular data structures
|
||||
// are detected and handled properly.
|
||||
func (d *dumpState) dump(v reflect.Value) {
|
||||
// Handle invalid reflect values immediately.
|
||||
kind := v.Kind()
|
||||
if kind == reflect.Invalid {
|
||||
d.w.Write(invalidAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle pointers specially.
|
||||
if kind == reflect.Ptr {
|
||||
d.indent()
|
||||
d.dumpPtr(v)
|
||||
return
|
||||
}
|
||||
|
||||
// Print type information unless already handled elsewhere.
|
||||
if !d.ignoreNextType {
|
||||
d.indent()
|
||||
d.w.Write(openParenBytes)
|
||||
d.w.Write([]byte(v.Type().String()))
|
||||
d.w.Write(closeParenBytes)
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
d.ignoreNextType = false
|
||||
|
||||
// Display length and capacity if the built-in len and cap functions
|
||||
// work with the value's kind and the len/cap itself is non-zero.
|
||||
valueLen, valueCap := 0, 0
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.Chan:
|
||||
valueLen, valueCap = v.Len(), v.Cap()
|
||||
case reflect.Map, reflect.String:
|
||||
valueLen = v.Len()
|
||||
}
|
||||
if valueLen != 0 || valueCap != 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
if valueLen != 0 {
|
||||
d.w.Write(lenEqualsBytes)
|
||||
printInt(d.w, int64(valueLen), 10)
|
||||
}
|
||||
if valueCap != 0 {
|
||||
if valueLen != 0 {
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
d.w.Write(capEqualsBytes)
|
||||
printInt(d.w, int64(valueCap), 10)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
|
||||
// Call Stringer/error interfaces if they exist and the handle methods flag
|
||||
// is enabled
|
||||
if !d.cs.DisableMethods {
|
||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||
if handled := handleMethods(d.cs, d.w, v); handled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Invalid:
|
||||
// Do nothing. We should never get here since invalid has already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Bool:
|
||||
printBool(d.w, v.Bool())
|
||||
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
printInt(d.w, v.Int(), 10)
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
printUint(d.w, v.Uint(), 10)
|
||||
|
||||
case reflect.Float32:
|
||||
printFloat(d.w, v.Float(), 32)
|
||||
|
||||
case reflect.Float64:
|
||||
printFloat(d.w, v.Float(), 64)
|
||||
|
||||
case reflect.Complex64:
|
||||
printComplex(d.w, v.Complex(), 32)
|
||||
|
||||
case reflect.Complex128:
|
||||
printComplex(d.w, v.Complex(), 64)
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
d.dumpSlice(v)
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.String:
|
||||
d.w.Write([]byte(strconv.Quote(v.String())))
|
||||
|
||||
case reflect.Interface:
|
||||
// The only time we should get here is for nil interfaces due to
|
||||
// unpackValue calls.
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// Do nothing. We should never get here since pointers have already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Map:
|
||||
// nil maps should be indicated as different than empty maps
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
numEntries := v.Len()
|
||||
keys := v.MapKeys()
|
||||
if d.cs.SortKeys {
|
||||
sortValues(keys, d.cs)
|
||||
}
|
||||
for i, key := range keys {
|
||||
d.dump(d.unpackValue(key))
|
||||
d.w.Write(colonSpaceBytes)
|
||||
d.ignoreNextIndent = true
|
||||
d.dump(d.unpackValue(v.MapIndex(key)))
|
||||
if i < (numEntries - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Struct:
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
vt := v.Type()
|
||||
numFields := v.NumField()
|
||||
for i := 0; i < numFields; i++ {
|
||||
d.indent()
|
||||
vtf := vt.Field(i)
|
||||
d.w.Write([]byte(vtf.Name))
|
||||
d.w.Write(colonSpaceBytes)
|
||||
d.ignoreNextIndent = true
|
||||
d.dump(d.unpackValue(v.Field(i)))
|
||||
if i < (numFields - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Uintptr:
|
||||
printHexPtr(d.w, uintptr(v.Uint()))
|
||||
|
||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||
printHexPtr(d.w, v.Pointer())
|
||||
|
||||
// There were not any other types at the time this code was written, but
|
||||
// fall back to letting the default fmt package handle it in case any new
|
||||
// types are added.
|
||||
default:
|
||||
if v.CanInterface() {
|
||||
fmt.Fprintf(d.w, "%v", v.Interface())
|
||||
} else {
|
||||
fmt.Fprintf(d.w, "%v", v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fdump is a helper function to consolidate the logic from the various public
|
||||
// methods which take varying writers and config states.
|
||||
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
||||
for _, arg := range a {
|
||||
if arg == nil {
|
||||
w.Write(interfaceBytes)
|
||||
w.Write(spaceBytes)
|
||||
w.Write(nilAngleBytes)
|
||||
w.Write(newlineBytes)
|
||||
continue
|
||||
}
|
||||
|
||||
d := dumpState{w: w, cs: cs}
|
||||
d.pointers = make(map[uintptr]int)
|
||||
d.dump(reflect.ValueOf(arg))
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func Fdump(w io.Writer, a ...interface{}) {
|
||||
fdump(&Config, w, a...)
|
||||
}
|
||||
|
||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||
// as Dump.
|
||||
func Sdump(a ...interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
fdump(&Config, &buf, a...)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by an exported package global,
|
||||
spew.Config. See ConfigState for options documentation.
|
||||
|
||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||
get the formatted result as a string.
|
||||
*/
|
||||
func Dump(a ...interface{}) {
|
||||
fdump(&Config, os.Stdout, a...)
|
||||
}
|
||||
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
Normal file
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
Normal file
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// supportedFlags is a list of all the character flags supported by fmt package.
|
||||
const supportedFlags = "0-+# "
|
||||
|
||||
// formatState implements the fmt.Formatter interface and contains information
|
||||
// about the state of a formatting operation. The NewFormatter function can
|
||||
// be used to get a new Formatter which can be used directly as arguments
|
||||
// in standard fmt package printing calls.
|
||||
type formatState struct {
|
||||
value interface{}
|
||||
fs fmt.State
|
||||
depth int
|
||||
pointers map[uintptr]int
|
||||
ignoreNextType bool
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// buildDefaultFormat recreates the original format string without precision
|
||||
// and width information to pass in to fmt.Sprintf in the case of an
|
||||
// unrecognized type. Unless new types are added to the language, this
|
||||
// function won't ever be called.
|
||||
func (f *formatState) buildDefaultFormat() (format string) {
|
||||
buf := bytes.NewBuffer(percentBytes)
|
||||
|
||||
for _, flag := range supportedFlags {
|
||||
if f.fs.Flag(int(flag)) {
|
||||
buf.WriteRune(flag)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteRune('v')
|
||||
|
||||
format = buf.String()
|
||||
return format
|
||||
}
|
||||
|
||||
// constructOrigFormat recreates the original format string including precision
|
||||
// and width information to pass along to the standard fmt package. This allows
|
||||
// automatic deferral of all format strings this package doesn't support.
|
||||
func (f *formatState) constructOrigFormat(verb rune) (format string) {
|
||||
buf := bytes.NewBuffer(percentBytes)
|
||||
|
||||
for _, flag := range supportedFlags {
|
||||
if f.fs.Flag(int(flag)) {
|
||||
buf.WriteRune(flag)
|
||||
}
|
||||
}
|
||||
|
||||
if width, ok := f.fs.Width(); ok {
|
||||
buf.WriteString(strconv.Itoa(width))
|
||||
}
|
||||
|
||||
if precision, ok := f.fs.Precision(); ok {
|
||||
buf.Write(precisionBytes)
|
||||
buf.WriteString(strconv.Itoa(precision))
|
||||
}
|
||||
|
||||
buf.WriteRune(verb)
|
||||
|
||||
format = buf.String()
|
||||
return format
|
||||
}
|
||||
|
||||
// unpackValue returns values inside of non-nil interfaces when possible and
|
||||
// ensures that types for values which have been unpacked from an interface
|
||||
// are displayed when the show types flag is also set.
|
||||
// This is useful for data types like structs, arrays, slices, and maps which
|
||||
// can contain varying types packed inside an interface.
|
||||
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Interface {
|
||||
f.ignoreNextType = false
|
||||
if !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
||||
func (f *formatState) formatPtr(v reflect.Value) {
|
||||
// Display nil if top level pointer is nil.
|
||||
showTypes := f.fs.Flag('#')
|
||||
if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove pointers at or below the current depth from map used to detect
|
||||
// circular refs.
|
||||
for k, depth := range f.pointers {
|
||||
if depth >= f.depth {
|
||||
delete(f.pointers, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep list of all dereferenced pointers to possibly show later.
|
||||
pointerChain := make([]uintptr, 0)
|
||||
|
||||
// Figure out how many levels of indirection there are by derferencing
|
||||
// pointers and unpacking interfaces down the chain while detecting circular
|
||||
// references.
|
||||
nilFound := false
|
||||
cycleFound := false
|
||||
indirects := 0
|
||||
ve := v
|
||||
for ve.Kind() == reflect.Ptr {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
indirects++
|
||||
addr := ve.Pointer()
|
||||
pointerChain = append(pointerChain, addr)
|
||||
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
||||
cycleFound = true
|
||||
indirects--
|
||||
break
|
||||
}
|
||||
f.pointers[addr] = f.depth
|
||||
|
||||
ve = ve.Elem()
|
||||
if ve.Kind() == reflect.Interface {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
ve = ve.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// Display type or indirection level depending on flags.
|
||||
if showTypes && !f.ignoreNextType {
|
||||
f.fs.Write(openParenBytes)
|
||||
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||
f.fs.Write([]byte(ve.Type().String()))
|
||||
f.fs.Write(closeParenBytes)
|
||||
} else {
|
||||
if nilFound || cycleFound {
|
||||
indirects += strings.Count(ve.Type().String(), "*")
|
||||
}
|
||||
f.fs.Write(openAngleBytes)
|
||||
f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
||||
f.fs.Write(closeAngleBytes)
|
||||
}
|
||||
|
||||
// Display pointer information depending on flags.
|
||||
if f.fs.Flag('+') && (len(pointerChain) > 0) {
|
||||
f.fs.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
f.fs.Write(pointerChainBytes)
|
||||
}
|
||||
printHexPtr(f.fs, addr)
|
||||
}
|
||||
f.fs.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// Display dereferenced value.
|
||||
switch {
|
||||
case nilFound == true:
|
||||
f.fs.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
f.fs.Write(circularShortBytes)
|
||||
|
||||
default:
|
||||
f.ignoreNextType = true
|
||||
f.format(ve)
|
||||
}
|
||||
}
|
||||
|
||||
// format is the main workhorse for providing the Formatter interface. It
|
||||
// uses the passed reflect value to figure out what kind of object we are
|
||||
// dealing with and formats it appropriately. It is a recursive function,
|
||||
// however circular data structures are detected and handled properly.
|
||||
func (f *formatState) format(v reflect.Value) {
|
||||
// Handle invalid reflect values immediately.
|
||||
kind := v.Kind()
|
||||
if kind == reflect.Invalid {
|
||||
f.fs.Write(invalidAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle pointers specially.
|
||||
if kind == reflect.Ptr {
|
||||
f.formatPtr(v)
|
||||
return
|
||||
}
|
||||
|
||||
// Print type information unless already handled elsewhere.
|
||||
if !f.ignoreNextType && f.fs.Flag('#') {
|
||||
f.fs.Write(openParenBytes)
|
||||
f.fs.Write([]byte(v.Type().String()))
|
||||
f.fs.Write(closeParenBytes)
|
||||
}
|
||||
f.ignoreNextType = false
|
||||
|
||||
// Call Stringer/error interfaces if they exist and the handle methods
|
||||
// flag is enabled.
|
||||
if !f.cs.DisableMethods {
|
||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||
if handled := handleMethods(f.cs, f.fs, v); handled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Invalid:
|
||||
// Do nothing. We should never get here since invalid has already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Bool:
|
||||
printBool(f.fs, v.Bool())
|
||||
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
printInt(f.fs, v.Int(), 10)
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
printUint(f.fs, v.Uint(), 10)
|
||||
|
||||
case reflect.Float32:
|
||||
printFloat(f.fs, v.Float(), 32)
|
||||
|
||||
case reflect.Float64:
|
||||
printFloat(f.fs, v.Float(), 64)
|
||||
|
||||
case reflect.Complex64:
|
||||
printComplex(f.fs, v.Complex(), 32)
|
||||
|
||||
case reflect.Complex128:
|
||||
printComplex(f.fs, v.Complex(), 64)
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
f.fs.Write(openBracketBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
numEntries := v.Len()
|
||||
for i := 0; i < numEntries; i++ {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(v.Index(i)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeBracketBytes)
|
||||
|
||||
case reflect.String:
|
||||
f.fs.Write([]byte(v.String()))
|
||||
|
||||
case reflect.Interface:
|
||||
// The only time we should get here is for nil interfaces due to
|
||||
// unpackValue calls.
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// Do nothing. We should never get here since pointers have already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Map:
|
||||
// nil maps should be indicated as different than empty maps
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
|
||||
f.fs.Write(openMapBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
keys := v.MapKeys()
|
||||
if f.cs.SortKeys {
|
||||
sortValues(keys, f.cs)
|
||||
}
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(key))
|
||||
f.fs.Write(colonBytes)
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(v.MapIndex(key)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeMapBytes)
|
||||
|
||||
case reflect.Struct:
|
||||
numFields := v.NumField()
|
||||
f.fs.Write(openBraceBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
vt := v.Type()
|
||||
for i := 0; i < numFields; i++ {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
vtf := vt.Field(i)
|
||||
if f.fs.Flag('+') || f.fs.Flag('#') {
|
||||
f.fs.Write([]byte(vtf.Name))
|
||||
f.fs.Write(colonBytes)
|
||||
}
|
||||
f.format(f.unpackValue(v.Field(i)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Uintptr:
|
||||
printHexPtr(f.fs, uintptr(v.Uint()))
|
||||
|
||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||
printHexPtr(f.fs, v.Pointer())
|
||||
|
||||
// There were not any other types at the time this code was written, but
|
||||
// fall back to letting the default fmt package handle it if any get added.
|
||||
default:
|
||||
format := f.buildDefaultFormat()
|
||||
if v.CanInterface() {
|
||||
fmt.Fprintf(f.fs, format, v.Interface())
|
||||
} else {
|
||||
fmt.Fprintf(f.fs, format, v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
|
||||
// details.
|
||||
func (f *formatState) Format(fs fmt.State, verb rune) {
|
||||
f.fs = fs
|
||||
|
||||
// Use standard formatting for verbs that are not v.
|
||||
if verb != 'v' {
|
||||
format := f.constructOrigFormat(verb)
|
||||
fmt.Fprintf(fs, format, f.value)
|
||||
return
|
||||
}
|
||||
|
||||
if f.value == nil {
|
||||
if fs.Flag('#') {
|
||||
fs.Write(interfaceBytes)
|
||||
}
|
||||
fs.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
f.format(reflect.ValueOf(f.value))
|
||||
}
|
||||
|
||||
// newFormatter is a helper function to consolidate the logic from the various
|
||||
// public methods which take varying config states.
|
||||
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
|
||||
fs := &formatState{value: v, cs: cs}
|
||||
fs.pointers = make(map[uintptr]int)
|
||||
return fs
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
printing functions. The formatter is useful for inline printing of smaller data
|
||||
types similar to the standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Typically this function shouldn't be called directly. It is much easier to make
|
||||
use of the custom formatter by calling one of the convenience functions such as
|
||||
Printf, Println, or Fprintf.
|
||||
*/
|
||||
func NewFormatter(v interface{}) fmt.Formatter {
|
||||
return newFormatter(&Config, v)
|
||||
}
|
||||
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
Normal file
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the formatted string as a value that satisfies error. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Errorf(format string, a ...interface{}) (err error) {
|
||||
return fmt.Errorf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(w, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(w, format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||
// passed with a default Formatter interface returned by NewFormatter. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Print(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Printf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Println(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprint(a ...interface{}) string {
|
||||
return fmt.Sprint(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||
// were passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprintln(a ...interface{}) string {
|
||||
return fmt.Sprintln(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||
// length with each argument converted to a default spew Formatter interface.
|
||||
func convertArgs(args []interface{}) (formatters []interface{}) {
|
||||
formatters = make([]interface{}, len(args))
|
||||
for index, arg := range args {
|
||||
formatters[index] = NewFormatter(arg)
|
||||
}
|
||||
return formatters
|
||||
}
|
||||
202
vendor/github.com/docker/distribution/LICENSE
generated
vendored
Normal file
202
vendor/github.com/docker/distribution/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
139
vendor/github.com/docker/distribution/digest/digest.go
generated
vendored
Normal file
139
vendor/github.com/docker/distribution/digest/digest.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// DigestSha256EmptyTar is the canonical sha256 digest of empty data
|
||||
DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
)
|
||||
|
||||
// Digest allows simple protection of hex formatted digest strings, prefixed
|
||||
// by their algorithm. Strings of type Digest have some guarantee of being in
|
||||
// the correct format and it provides quick access to the components of a
|
||||
// digest string.
|
||||
//
|
||||
// The following is an example of the contents of Digest types:
|
||||
//
|
||||
// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
|
||||
//
|
||||
// This allows to abstract the digest behind this type and work only in those
|
||||
// terms.
|
||||
type Digest string
|
||||
|
||||
// NewDigest returns a Digest from alg and a hash.Hash object.
|
||||
func NewDigest(alg Algorithm, h hash.Hash) Digest {
|
||||
return NewDigestFromBytes(alg, h.Sum(nil))
|
||||
}
|
||||
|
||||
// NewDigestFromBytes returns a new digest from the byte contents of p.
|
||||
// Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...)
|
||||
// functions. This is also useful for rebuilding digests from binary
|
||||
// serializations.
|
||||
func NewDigestFromBytes(alg Algorithm, p []byte) Digest {
|
||||
return Digest(fmt.Sprintf("%s:%x", alg, p))
|
||||
}
|
||||
|
||||
// NewDigestFromHex returns a Digest from alg and a the hex encoded digest.
|
||||
func NewDigestFromHex(alg, hex string) Digest {
|
||||
return Digest(fmt.Sprintf("%s:%s", alg, hex))
|
||||
}
|
||||
|
||||
// DigestRegexp matches valid digest types.
|
||||
var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`)
|
||||
|
||||
// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match.
|
||||
var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`)
|
||||
|
||||
var (
|
||||
// ErrDigestInvalidFormat returned when digest format invalid.
|
||||
ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format")
|
||||
|
||||
// ErrDigestInvalidLength returned when digest has invalid length.
|
||||
ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length")
|
||||
|
||||
// ErrDigestUnsupported returned when the digest algorithm is unsupported.
|
||||
ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm")
|
||||
)
|
||||
|
||||
// ParseDigest parses s and returns the validated digest object. An error will
|
||||
// be returned if the format is invalid.
|
||||
func ParseDigest(s string) (Digest, error) {
|
||||
d := Digest(s)
|
||||
|
||||
return d, d.Validate()
|
||||
}
|
||||
|
||||
// FromReader returns the most valid digest for the underlying content using
|
||||
// the canonical digest algorithm.
|
||||
func FromReader(rd io.Reader) (Digest, error) {
|
||||
return Canonical.FromReader(rd)
|
||||
}
|
||||
|
||||
// FromBytes digests the input and returns a Digest.
|
||||
func FromBytes(p []byte) Digest {
|
||||
return Canonical.FromBytes(p)
|
||||
}
|
||||
|
||||
// Validate checks that the contents of d is a valid digest, returning an
|
||||
// error if not.
|
||||
func (d Digest) Validate() error {
|
||||
s := string(d)
|
||||
|
||||
if !DigestRegexpAnchored.MatchString(s) {
|
||||
return ErrDigestInvalidFormat
|
||||
}
|
||||
|
||||
i := strings.Index(s, ":")
|
||||
if i < 0 {
|
||||
return ErrDigestInvalidFormat
|
||||
}
|
||||
|
||||
// case: "sha256:" with no hex.
|
||||
if i+1 == len(s) {
|
||||
return ErrDigestInvalidFormat
|
||||
}
|
||||
|
||||
switch algorithm := Algorithm(s[:i]); algorithm {
|
||||
case SHA256, SHA384, SHA512:
|
||||
if algorithm.Size()*2 != len(s[i+1:]) {
|
||||
return ErrDigestInvalidLength
|
||||
}
|
||||
break
|
||||
default:
|
||||
return ErrDigestUnsupported
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Algorithm returns the algorithm portion of the digest. This will panic if
|
||||
// the underlying digest is not in a valid format.
|
||||
func (d Digest) Algorithm() Algorithm {
|
||||
return Algorithm(d[:d.sepIndex()])
|
||||
}
|
||||
|
||||
// Hex returns the hex digest portion of the digest. This will panic if the
|
||||
// underlying digest is not in a valid format.
|
||||
func (d Digest) Hex() string {
|
||||
return string(d[d.sepIndex()+1:])
|
||||
}
|
||||
|
||||
func (d Digest) String() string {
|
||||
return string(d)
|
||||
}
|
||||
|
||||
func (d Digest) sepIndex() int {
|
||||
i := strings.Index(string(d), ":")
|
||||
|
||||
if i < 0 {
|
||||
panic("could not find ':' in digest: " + d)
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
155
vendor/github.com/docker/distribution/digest/digester.go
generated
vendored
Normal file
155
vendor/github.com/docker/distribution/digest/digester.go
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Algorithm identifies and implementation of a digester by an identifier.
|
||||
// Note the that this defines both the hash algorithm used and the string
|
||||
// encoding.
|
||||
type Algorithm string
|
||||
|
||||
// supported digest types
|
||||
const (
|
||||
SHA256 Algorithm = "sha256" // sha256 with hex encoding
|
||||
SHA384 Algorithm = "sha384" // sha384 with hex encoding
|
||||
SHA512 Algorithm = "sha512" // sha512 with hex encoding
|
||||
|
||||
// Canonical is the primary digest algorithm used with the distribution
|
||||
// project. Other digests may be used but this one is the primary storage
|
||||
// digest.
|
||||
Canonical = SHA256
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO(stevvooe): Follow the pattern of the standard crypto package for
|
||||
// registration of digests. Effectively, we are a registerable set and
|
||||
// common symbol access.
|
||||
|
||||
// algorithms maps values to hash.Hash implementations. Other algorithms
|
||||
// may be available but they cannot be calculated by the digest package.
|
||||
algorithms = map[Algorithm]crypto.Hash{
|
||||
SHA256: crypto.SHA256,
|
||||
SHA384: crypto.SHA384,
|
||||
SHA512: crypto.SHA512,
|
||||
}
|
||||
)
|
||||
|
||||
// Available returns true if the digest type is available for use. If this
|
||||
// returns false, New and Hash will return nil.
|
||||
func (a Algorithm) Available() bool {
|
||||
h, ok := algorithms[a]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// check availability of the hash, as well
|
||||
return h.Available()
|
||||
}
|
||||
|
||||
func (a Algorithm) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// Size returns number of bytes returned by the hash.
|
||||
func (a Algorithm) Size() int {
|
||||
h, ok := algorithms[a]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return h.Size()
|
||||
}
|
||||
|
||||
// Set implemented to allow use of Algorithm as a command line flag.
|
||||
func (a *Algorithm) Set(value string) error {
|
||||
if value == "" {
|
||||
*a = Canonical
|
||||
} else {
|
||||
// just do a type conversion, support is queried with Available.
|
||||
*a = Algorithm(value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New returns a new digester for the specified algorithm. If the algorithm
|
||||
// does not have a digester implementation, nil will be returned. This can be
|
||||
// checked by calling Available before calling New.
|
||||
func (a Algorithm) New() Digester {
|
||||
return &digester{
|
||||
alg: a,
|
||||
hash: a.Hash(),
|
||||
}
|
||||
}
|
||||
|
||||
// Hash returns a new hash as used by the algorithm. If not available, the
|
||||
// method will panic. Check Algorithm.Available() before calling.
|
||||
func (a Algorithm) Hash() hash.Hash {
|
||||
if !a.Available() {
|
||||
// NOTE(stevvooe): A missing hash is usually a programming error that
|
||||
// must be resolved at compile time. We don't import in the digest
|
||||
// package to allow users to choose their hash implementation (such as
|
||||
// when using stevvooe/resumable or a hardware accelerated package).
|
||||
//
|
||||
// Applications that may want to resolve the hash at runtime should
|
||||
// call Algorithm.Available before call Algorithm.Hash().
|
||||
panic(fmt.Sprintf("%v not available (make sure it is imported)", a))
|
||||
}
|
||||
|
||||
return algorithms[a].New()
|
||||
}
|
||||
|
||||
// FromReader returns the digest of the reader using the algorithm.
|
||||
func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
|
||||
digester := a.New()
|
||||
|
||||
if _, err := io.Copy(digester.Hash(), rd); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return digester.Digest(), nil
|
||||
}
|
||||
|
||||
// FromBytes digests the input and returns a Digest.
|
||||
func (a Algorithm) FromBytes(p []byte) Digest {
|
||||
digester := a.New()
|
||||
|
||||
if _, err := digester.Hash().Write(p); err != nil {
|
||||
// Writes to a Hash should never fail. None of the existing
|
||||
// hash implementations in the stdlib or hashes vendored
|
||||
// here can return errors from Write. Having a panic in this
|
||||
// condition instead of having FromBytes return an error value
|
||||
// avoids unnecessary error handling paths in all callers.
|
||||
panic("write to hash function returned error: " + err.Error())
|
||||
}
|
||||
|
||||
return digester.Digest()
|
||||
}
|
||||
|
||||
// TODO(stevvooe): Allow resolution of verifiers using the digest type and
|
||||
// this registration system.
|
||||
|
||||
// Digester calculates the digest of written data. Writes should go directly
|
||||
// to the return value of Hash, while calling Digest will return the current
|
||||
// value of the digest.
|
||||
type Digester interface {
|
||||
Hash() hash.Hash // provides direct access to underlying hash instance.
|
||||
Digest() Digest
|
||||
}
|
||||
|
||||
// digester provides a simple digester definition that embeds a hasher.
|
||||
type digester struct {
|
||||
alg Algorithm
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
func (d *digester) Hash() hash.Hash {
|
||||
return d.hash
|
||||
}
|
||||
|
||||
func (d *digester) Digest() Digest {
|
||||
return NewDigest(d.alg, d.hash)
|
||||
}
|
||||
42
vendor/github.com/docker/distribution/digest/doc.go
generated
vendored
Normal file
42
vendor/github.com/docker/distribution/digest/doc.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// Package digest provides a generalized type to opaquely represent message
|
||||
// digests and their operations within the registry. The Digest type is
|
||||
// designed to serve as a flexible identifier in a content-addressable system.
|
||||
// More importantly, it provides tools and wrappers to work with
|
||||
// hash.Hash-based digests with little effort.
|
||||
//
|
||||
// Basics
|
||||
//
|
||||
// The format of a digest is simply a string with two parts, dubbed the
|
||||
// "algorithm" and the "digest", separated by a colon:
|
||||
//
|
||||
// <algorithm>:<digest>
|
||||
//
|
||||
// An example of a sha256 digest representation follows:
|
||||
//
|
||||
// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
|
||||
//
|
||||
// In this case, the string "sha256" is the algorithm and the hex bytes are
|
||||
// the "digest".
|
||||
//
|
||||
// Because the Digest type is simply a string, once a valid Digest is
|
||||
// obtained, comparisons are cheap, quick and simple to express with the
|
||||
// standard equality operator.
|
||||
//
|
||||
// Verification
|
||||
//
|
||||
// The main benefit of using the Digest type is simple verification against a
|
||||
// given digest. The Verifier interface, modeled after the stdlib hash.Hash
|
||||
// interface, provides a common write sink for digest verification. After
|
||||
// writing is complete, calling the Verifier.Verified method will indicate
|
||||
// whether or not the stream of bytes matches the target digest.
|
||||
//
|
||||
// Missing Features
|
||||
//
|
||||
// In addition to the above, we intend to add the following features to this
|
||||
// package:
|
||||
//
|
||||
// 1. A Digester type that supports write sink digest calculation.
|
||||
//
|
||||
// 2. Suspend and resume of ongoing digest calculations to support efficient digest verification in the registry.
|
||||
//
|
||||
package digest
|
||||
245
vendor/github.com/docker/distribution/digest/set.go
generated
vendored
Normal file
245
vendor/github.com/docker/distribution/digest/set.go
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDigestNotFound is used when a matching digest
|
||||
// could not be found in a set.
|
||||
ErrDigestNotFound = errors.New("digest not found")
|
||||
|
||||
// ErrDigestAmbiguous is used when multiple digests
|
||||
// are found in a set. None of the matching digests
|
||||
// should be considered valid matches.
|
||||
ErrDigestAmbiguous = errors.New("ambiguous digest string")
|
||||
)
|
||||
|
||||
// Set is used to hold a unique set of digests which
|
||||
// may be easily referenced by easily referenced by a string
|
||||
// representation of the digest as well as short representation.
|
||||
// The uniqueness of the short representation is based on other
|
||||
// digests in the set. If digests are omitted from this set,
|
||||
// collisions in a larger set may not be detected, therefore it
|
||||
// is important to always do short representation lookups on
|
||||
// the complete set of digests. To mitigate collisions, an
|
||||
// appropriately long short code should be used.
|
||||
type Set struct {
|
||||
mutex sync.RWMutex
|
||||
entries digestEntries
|
||||
}
|
||||
|
||||
// NewSet creates an empty set of digests
|
||||
// which may have digests added.
|
||||
func NewSet() *Set {
|
||||
return &Set{
|
||||
entries: digestEntries{},
|
||||
}
|
||||
}
|
||||
|
||||
// checkShortMatch checks whether two digests match as either whole
|
||||
// values or short values. This function does not test equality,
|
||||
// rather whether the second value could match against the first
|
||||
// value.
|
||||
func checkShortMatch(alg Algorithm, hex, shortAlg, shortHex string) bool {
|
||||
if len(hex) == len(shortHex) {
|
||||
if hex != shortHex {
|
||||
return false
|
||||
}
|
||||
if len(shortAlg) > 0 && string(alg) != shortAlg {
|
||||
return false
|
||||
}
|
||||
} else if !strings.HasPrefix(hex, shortHex) {
|
||||
return false
|
||||
} else if len(shortAlg) > 0 && string(alg) != shortAlg {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Lookup looks for a digest matching the given string representation.
|
||||
// If no digests could be found ErrDigestNotFound will be returned
|
||||
// with an empty digest value. If multiple matches are found
|
||||
// ErrDigestAmbiguous will be returned with an empty digest value.
|
||||
func (dst *Set) Lookup(d string) (Digest, error) {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
if len(dst.entries) == 0 {
|
||||
return "", ErrDigestNotFound
|
||||
}
|
||||
var (
|
||||
searchFunc func(int) bool
|
||||
alg Algorithm
|
||||
hex string
|
||||
)
|
||||
dgst, err := ParseDigest(d)
|
||||
if err == ErrDigestInvalidFormat {
|
||||
hex = d
|
||||
searchFunc = func(i int) bool {
|
||||
return dst.entries[i].val >= d
|
||||
}
|
||||
} else {
|
||||
hex = dgst.Hex()
|
||||
alg = dgst.Algorithm()
|
||||
searchFunc = func(i int) bool {
|
||||
if dst.entries[i].val == hex {
|
||||
return dst.entries[i].alg >= alg
|
||||
}
|
||||
return dst.entries[i].val >= hex
|
||||
}
|
||||
}
|
||||
idx := sort.Search(len(dst.entries), searchFunc)
|
||||
if idx == len(dst.entries) || !checkShortMatch(dst.entries[idx].alg, dst.entries[idx].val, string(alg), hex) {
|
||||
return "", ErrDigestNotFound
|
||||
}
|
||||
if dst.entries[idx].alg == alg && dst.entries[idx].val == hex {
|
||||
return dst.entries[idx].digest, nil
|
||||
}
|
||||
if idx+1 < len(dst.entries) && checkShortMatch(dst.entries[idx+1].alg, dst.entries[idx+1].val, string(alg), hex) {
|
||||
return "", ErrDigestAmbiguous
|
||||
}
|
||||
|
||||
return dst.entries[idx].digest, nil
|
||||
}
|
||||
|
||||
// Add adds the given digest to the set. An error will be returned
|
||||
// if the given digest is invalid. If the digest already exists in the
|
||||
// set, this operation will be a no-op.
|
||||
func (dst *Set) Add(d Digest) error {
|
||||
if err := d.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
dst.mutex.Lock()
|
||||
defer dst.mutex.Unlock()
|
||||
entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
|
||||
searchFunc := func(i int) bool {
|
||||
if dst.entries[i].val == entry.val {
|
||||
return dst.entries[i].alg >= entry.alg
|
||||
}
|
||||
return dst.entries[i].val >= entry.val
|
||||
}
|
||||
idx := sort.Search(len(dst.entries), searchFunc)
|
||||
if idx == len(dst.entries) {
|
||||
dst.entries = append(dst.entries, entry)
|
||||
return nil
|
||||
} else if dst.entries[idx].digest == d {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries := append(dst.entries, nil)
|
||||
copy(entries[idx+1:], entries[idx:len(entries)-1])
|
||||
entries[idx] = entry
|
||||
dst.entries = entries
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the given digest from the set. An err will be
|
||||
// returned if the given digest is invalid. If the digest does
|
||||
// not exist in the set, this operation will be a no-op.
|
||||
func (dst *Set) Remove(d Digest) error {
|
||||
if err := d.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
dst.mutex.Lock()
|
||||
defer dst.mutex.Unlock()
|
||||
entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
|
||||
searchFunc := func(i int) bool {
|
||||
if dst.entries[i].val == entry.val {
|
||||
return dst.entries[i].alg >= entry.alg
|
||||
}
|
||||
return dst.entries[i].val >= entry.val
|
||||
}
|
||||
idx := sort.Search(len(dst.entries), searchFunc)
|
||||
// Not found if idx is after or value at idx is not digest
|
||||
if idx == len(dst.entries) || dst.entries[idx].digest != d {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries := dst.entries
|
||||
copy(entries[idx:], entries[idx+1:])
|
||||
entries = entries[:len(entries)-1]
|
||||
dst.entries = entries
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// All returns all the digests in the set
|
||||
func (dst *Set) All() []Digest {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
retValues := make([]Digest, len(dst.entries))
|
||||
for i := range dst.entries {
|
||||
retValues[i] = dst.entries[i].digest
|
||||
}
|
||||
|
||||
return retValues
|
||||
}
|
||||
|
||||
// ShortCodeTable returns a map of Digest to unique short codes. The
|
||||
// length represents the minimum value, the maximum length may be the
|
||||
// entire value of digest if uniqueness cannot be achieved without the
|
||||
// full value. This function will attempt to make short codes as short
|
||||
// as possible to be unique.
|
||||
func ShortCodeTable(dst *Set, length int) map[Digest]string {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
m := make(map[Digest]string, len(dst.entries))
|
||||
l := length
|
||||
resetIdx := 0
|
||||
for i := 0; i < len(dst.entries); i++ {
|
||||
var short string
|
||||
extended := true
|
||||
for extended {
|
||||
extended = false
|
||||
if len(dst.entries[i].val) <= l {
|
||||
short = dst.entries[i].digest.String()
|
||||
} else {
|
||||
short = dst.entries[i].val[:l]
|
||||
for j := i + 1; j < len(dst.entries); j++ {
|
||||
if checkShortMatch(dst.entries[j].alg, dst.entries[j].val, "", short) {
|
||||
if j > resetIdx {
|
||||
resetIdx = j
|
||||
}
|
||||
extended = true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if extended {
|
||||
l++
|
||||
}
|
||||
}
|
||||
}
|
||||
m[dst.entries[i].digest] = short
|
||||
if i >= resetIdx {
|
||||
l = length
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type digestEntry struct {
|
||||
alg Algorithm
|
||||
val string
|
||||
digest Digest
|
||||
}
|
||||
|
||||
type digestEntries []*digestEntry
|
||||
|
||||
func (d digestEntries) Len() int {
|
||||
return len(d)
|
||||
}
|
||||
|
||||
func (d digestEntries) Less(i, j int) bool {
|
||||
if d[i].val != d[j].val {
|
||||
return d[i].val < d[j].val
|
||||
}
|
||||
return d[i].alg < d[j].alg
|
||||
}
|
||||
|
||||
func (d digestEntries) Swap(i, j int) {
|
||||
d[i], d[j] = d[j], d[i]
|
||||
}
|
||||
44
vendor/github.com/docker/distribution/digest/verifiers.go
generated
vendored
Normal file
44
vendor/github.com/docker/distribution/digest/verifiers.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Verifier presents a general verification interface to be used with message
|
||||
// digests and other byte stream verifications. Users instantiate a Verifier
|
||||
// from one of the various methods, write the data under test to it then check
|
||||
// the result with the Verified method.
|
||||
type Verifier interface {
|
||||
io.Writer
|
||||
|
||||
// Verified will return true if the content written to Verifier matches
|
||||
// the digest.
|
||||
Verified() bool
|
||||
}
|
||||
|
||||
// NewDigestVerifier returns a verifier that compares the written bytes
|
||||
// against a passed in digest.
|
||||
func NewDigestVerifier(d Digest) (Verifier, error) {
|
||||
if err := d.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hashVerifier{
|
||||
hash: d.Algorithm().Hash(),
|
||||
digest: d,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type hashVerifier struct {
|
||||
digest Digest
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
func (hv hashVerifier) Write(p []byte) (n int, err error) {
|
||||
return hv.hash.Write(p)
|
||||
}
|
||||
|
||||
func (hv hashVerifier) Verified() bool {
|
||||
return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash)
|
||||
}
|
||||
334
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
Normal file
334
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
Normal file
@@ -0,0 +1,334 @@
|
||||
// Package reference provides a general type to represent any way of referencing images within the registry.
|
||||
// Its main purpose is to abstract tags and digests (content-addressable hash).
|
||||
//
|
||||
// Grammar
|
||||
//
|
||||
// reference := name [ ":" tag ] [ "@" digest ]
|
||||
// name := [hostname '/'] component ['/' component]*
|
||||
// hostname := hostcomponent ['.' hostcomponent]* [':' port-number]
|
||||
// hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
||||
// port-number := /[0-9]+/
|
||||
// component := alpha-numeric [separator alpha-numeric]*
|
||||
// alpha-numeric := /[a-z0-9]+/
|
||||
// separator := /[_.]|__|[-]*/
|
||||
//
|
||||
// tag := /[\w][\w.-]{0,127}/
|
||||
//
|
||||
// digest := digest-algorithm ":" digest-hex
|
||||
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]
|
||||
// digest-algorithm-separator := /[+.-_]/
|
||||
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
|
||||
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
||||
package reference
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
)
|
||||
|
||||
const (
|
||||
// NameTotalLengthMax is the maximum total number of characters in a repository name.
|
||||
NameTotalLengthMax = 255
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference.
|
||||
ErrReferenceInvalidFormat = errors.New("invalid reference format")
|
||||
|
||||
// ErrTagInvalidFormat represents an error while trying to parse a string as a tag.
|
||||
ErrTagInvalidFormat = errors.New("invalid tag format")
|
||||
|
||||
// ErrDigestInvalidFormat represents an error while trying to parse a string as a tag.
|
||||
ErrDigestInvalidFormat = errors.New("invalid digest format")
|
||||
|
||||
// ErrNameEmpty is returned for empty, invalid repository names.
|
||||
ErrNameEmpty = errors.New("repository name must have at least one component")
|
||||
|
||||
// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax.
|
||||
ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
|
||||
)
|
||||
|
||||
// Reference is an opaque object reference identifier that may include
|
||||
// modifiers such as a hostname, name, tag, and digest.
|
||||
type Reference interface {
|
||||
// String returns the full reference
|
||||
String() string
|
||||
}
|
||||
|
||||
// Field provides a wrapper type for resolving correct reference types when
|
||||
// working with encoding.
|
||||
type Field struct {
|
||||
reference Reference
|
||||
}
|
||||
|
||||
// AsField wraps a reference in a Field for encoding.
|
||||
func AsField(reference Reference) Field {
|
||||
return Field{reference}
|
||||
}
|
||||
|
||||
// Reference unwraps the reference type from the field to
|
||||
// return the Reference object. This object should be
|
||||
// of the appropriate type to further check for different
|
||||
// reference types.
|
||||
func (f Field) Reference() Reference {
|
||||
return f.reference
|
||||
}
|
||||
|
||||
// MarshalText serializes the field to byte text which
|
||||
// is the string of the reference.
|
||||
func (f Field) MarshalText() (p []byte, err error) {
|
||||
return []byte(f.reference.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText parses text bytes by invoking the
|
||||
// reference parser to ensure the appropriately
|
||||
// typed reference object is wrapped by field.
|
||||
func (f *Field) UnmarshalText(p []byte) error {
|
||||
r, err := Parse(string(p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.reference = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// Named is an object with a full name
|
||||
type Named interface {
|
||||
Reference
|
||||
Name() string
|
||||
}
|
||||
|
||||
// Tagged is an object which has a tag
|
||||
type Tagged interface {
|
||||
Reference
|
||||
Tag() string
|
||||
}
|
||||
|
||||
// NamedTagged is an object including a name and tag.
|
||||
type NamedTagged interface {
|
||||
Named
|
||||
Tag() string
|
||||
}
|
||||
|
||||
// Digested is an object which has a digest
|
||||
// in which it can be referenced by
|
||||
type Digested interface {
|
||||
Reference
|
||||
Digest() digest.Digest
|
||||
}
|
||||
|
||||
// Canonical reference is an object with a fully unique
|
||||
// name including a name with hostname and digest
|
||||
type Canonical interface {
|
||||
Named
|
||||
Digest() digest.Digest
|
||||
}
|
||||
|
||||
// SplitHostname splits a named reference into a
|
||||
// hostname and name string. If no valid hostname is
|
||||
// found, the hostname is empty and the full value
|
||||
// is returned as name
|
||||
func SplitHostname(named Named) (string, string) {
|
||||
name := named.Name()
|
||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||
if match == nil || len(match) != 3 {
|
||||
return "", name
|
||||
}
|
||||
return match[1], match[2]
|
||||
}
|
||||
|
||||
// Parse parses s and returns a syntactically valid Reference.
|
||||
// If an error was encountered it is returned, along with a nil Reference.
|
||||
// NOTE: Parse will not handle short digests.
|
||||
func Parse(s string) (Reference, error) {
|
||||
matches := ReferenceRegexp.FindStringSubmatch(s)
|
||||
if matches == nil {
|
||||
if s == "" {
|
||||
return nil, ErrNameEmpty
|
||||
}
|
||||
// TODO(dmcgowan): Provide more specific and helpful error
|
||||
return nil, ErrReferenceInvalidFormat
|
||||
}
|
||||
|
||||
if len(matches[1]) > NameTotalLengthMax {
|
||||
return nil, ErrNameTooLong
|
||||
}
|
||||
|
||||
ref := reference{
|
||||
name: matches[1],
|
||||
tag: matches[2],
|
||||
}
|
||||
if matches[3] != "" {
|
||||
var err error
|
||||
ref.digest, err = digest.ParseDigest(matches[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
r := getBestReferenceType(ref)
|
||||
if r == nil {
|
||||
return nil, ErrNameEmpty
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// ParseNamed parses s and returns a syntactically valid reference implementing
|
||||
// the Named interface. The reference must have a name, otherwise an error is
|
||||
// returned.
|
||||
// If an error was encountered it is returned, along with a nil Reference.
|
||||
// NOTE: ParseNamed will not handle short digests.
|
||||
func ParseNamed(s string) (Named, error) {
|
||||
ref, err := Parse(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
named, isNamed := ref.(Named)
|
||||
if !isNamed {
|
||||
return nil, fmt.Errorf("reference %s has no name", ref.String())
|
||||
}
|
||||
return named, nil
|
||||
}
|
||||
|
||||
// WithName returns a named object representing the given string. If the input
|
||||
// is invalid ErrReferenceInvalidFormat will be returned.
|
||||
func WithName(name string) (Named, error) {
|
||||
if len(name) > NameTotalLengthMax {
|
||||
return nil, ErrNameTooLong
|
||||
}
|
||||
if !anchoredNameRegexp.MatchString(name) {
|
||||
return nil, ErrReferenceInvalidFormat
|
||||
}
|
||||
return repository(name), nil
|
||||
}
|
||||
|
||||
// WithTag combines the name from "name" and the tag from "tag" to form a
|
||||
// reference incorporating both the name and the tag.
|
||||
func WithTag(name Named, tag string) (NamedTagged, error) {
|
||||
if !anchoredTagRegexp.MatchString(tag) {
|
||||
return nil, ErrTagInvalidFormat
|
||||
}
|
||||
return taggedReference{
|
||||
name: name.Name(),
|
||||
tag: tag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithDigest combines the name from "name" and the digest from "digest" to form
|
||||
// a reference incorporating both the name and the digest.
|
||||
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
||||
if !anchoredDigestRegexp.MatchString(digest.String()) {
|
||||
return nil, ErrDigestInvalidFormat
|
||||
}
|
||||
return canonicalReference{
|
||||
name: name.Name(),
|
||||
digest: digest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getBestReferenceType(ref reference) Reference {
|
||||
if ref.name == "" {
|
||||
// Allow digest only references
|
||||
if ref.digest != "" {
|
||||
return digestReference(ref.digest)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if ref.tag == "" {
|
||||
if ref.digest != "" {
|
||||
return canonicalReference{
|
||||
name: ref.name,
|
||||
digest: ref.digest,
|
||||
}
|
||||
}
|
||||
return repository(ref.name)
|
||||
}
|
||||
if ref.digest == "" {
|
||||
return taggedReference{
|
||||
name: ref.name,
|
||||
tag: ref.tag,
|
||||
}
|
||||
}
|
||||
|
||||
return ref
|
||||
}
|
||||
|
||||
type reference struct {
|
||||
name string
|
||||
tag string
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
func (r reference) String() string {
|
||||
return r.name + ":" + r.tag + "@" + r.digest.String()
|
||||
}
|
||||
|
||||
func (r reference) Name() string {
|
||||
return r.name
|
||||
}
|
||||
|
||||
func (r reference) Tag() string {
|
||||
return r.tag
|
||||
}
|
||||
|
||||
func (r reference) Digest() digest.Digest {
|
||||
return r.digest
|
||||
}
|
||||
|
||||
type repository string
|
||||
|
||||
func (r repository) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
func (r repository) Name() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
type digestReference digest.Digest
|
||||
|
||||
func (d digestReference) String() string {
|
||||
return d.String()
|
||||
}
|
||||
|
||||
func (d digestReference) Digest() digest.Digest {
|
||||
return digest.Digest(d)
|
||||
}
|
||||
|
||||
type taggedReference struct {
|
||||
name string
|
||||
tag string
|
||||
}
|
||||
|
||||
func (t taggedReference) String() string {
|
||||
return t.name + ":" + t.tag
|
||||
}
|
||||
|
||||
func (t taggedReference) Name() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t taggedReference) Tag() string {
|
||||
return t.tag
|
||||
}
|
||||
|
||||
type canonicalReference struct {
|
||||
name string
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
func (c canonicalReference) String() string {
|
||||
return c.name + "@" + c.digest.String()
|
||||
}
|
||||
|
||||
func (c canonicalReference) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c canonicalReference) Digest() digest.Digest {
|
||||
return c.digest
|
||||
}
|
||||
124
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
Normal file
124
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
package reference
|
||||
|
||||
import "regexp"
|
||||
|
||||
var (
|
||||
// alphaNumericRegexp defines the alpha numeric atom, typically a
|
||||
// component of names. This only allows lower case characters and digits.
|
||||
alphaNumericRegexp = match(`[a-z0-9]+`)
|
||||
|
||||
// separatorRegexp defines the separators allowed to be embedded in name
|
||||
// components. This allow one period, one or two underscore and multiple
|
||||
// dashes.
|
||||
separatorRegexp = match(`(?:[._]|__|[-]*)`)
|
||||
|
||||
// nameComponentRegexp restricts registry path component names to start
|
||||
// with at least one letter or number, with following parts able to be
|
||||
// separated by one period, one or two underscore and multiple dashes.
|
||||
nameComponentRegexp = expression(
|
||||
alphaNumericRegexp,
|
||||
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
||||
|
||||
// hostnameComponentRegexp restricts the registry hostname component of a
|
||||
// repository name to start with a component as defined by hostnameRegexp
|
||||
// and followed by an optional port.
|
||||
hostnameComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
||||
|
||||
// hostnameRegexp defines the structure of potential hostname components
|
||||
// that may be part of image names. This is purposely a subset of what is
|
||||
// allowed by DNS to ensure backwards compatibility with Docker image
|
||||
// names.
|
||||
hostnameRegexp = expression(
|
||||
hostnameComponentRegexp,
|
||||
optional(repeated(literal(`.`), hostnameComponentRegexp)),
|
||||
optional(literal(`:`), match(`[0-9]+`)))
|
||||
|
||||
// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
||||
TagRegexp = match(`[\w][\w.-]{0,127}`)
|
||||
|
||||
// anchoredTagRegexp matches valid tag names, anchored at the start and
|
||||
// end of the matched string.
|
||||
anchoredTagRegexp = anchored(TagRegexp)
|
||||
|
||||
// DigestRegexp matches valid digests.
|
||||
DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`)
|
||||
|
||||
// anchoredDigestRegexp matches valid digests, anchored at the start and
|
||||
// end of the matched string.
|
||||
anchoredDigestRegexp = anchored(DigestRegexp)
|
||||
|
||||
// NameRegexp is the format for the name component of references. The
|
||||
// regexp has capturing groups for the hostname and name part omitting
|
||||
// the separating forward slash from either.
|
||||
NameRegexp = expression(
|
||||
optional(hostnameRegexp, literal(`/`)),
|
||||
nameComponentRegexp,
|
||||
optional(repeated(literal(`/`), nameComponentRegexp)))
|
||||
|
||||
// anchoredNameRegexp is used to parse a name value, capturing the
|
||||
// hostname and trailing components.
|
||||
anchoredNameRegexp = anchored(
|
||||
optional(capture(hostnameRegexp), literal(`/`)),
|
||||
capture(nameComponentRegexp,
|
||||
optional(repeated(literal(`/`), nameComponentRegexp))))
|
||||
|
||||
// ReferenceRegexp is the full supported format of a reference. The regexp
|
||||
// is anchored and has capturing groups for name, tag, and digest
|
||||
// components.
|
||||
ReferenceRegexp = anchored(capture(NameRegexp),
|
||||
optional(literal(":"), capture(TagRegexp)),
|
||||
optional(literal("@"), capture(DigestRegexp)))
|
||||
)
|
||||
|
||||
// match compiles the string to a regular expression.
|
||||
var match = regexp.MustCompile
|
||||
|
||||
// literal compiles s into a literal regular expression, escaping any regexp
|
||||
// reserved characters.
|
||||
func literal(s string) *regexp.Regexp {
|
||||
re := match(regexp.QuoteMeta(s))
|
||||
|
||||
if _, complete := re.LiteralPrefix(); !complete {
|
||||
panic("must be a literal")
|
||||
}
|
||||
|
||||
return re
|
||||
}
|
||||
|
||||
// expression defines a full expression, where each regular expression must
|
||||
// follow the previous.
|
||||
func expression(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
var s string
|
||||
for _, re := range res {
|
||||
s += re.String()
|
||||
}
|
||||
|
||||
return match(s)
|
||||
}
|
||||
|
||||
// optional wraps the expression in a non-capturing group and makes the
|
||||
// production optional.
|
||||
func optional(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(group(expression(res...)).String() + `?`)
|
||||
}
|
||||
|
||||
// repeated wraps the regexp in a non-capturing group to get one or more
|
||||
// matches.
|
||||
func repeated(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(group(expression(res...)).String() + `+`)
|
||||
}
|
||||
|
||||
// group wraps the regexp in a non-capturing group.
|
||||
func group(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(`(?:` + expression(res...).String() + `)`)
|
||||
}
|
||||
|
||||
// capture wraps the expression in a capturing group.
|
||||
func capture(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(`(` + expression(res...).String() + `)`)
|
||||
}
|
||||
|
||||
// anchored anchors the regular expression by adding start and end delimiters.
|
||||
func anchored(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(`^` + expression(res...).String() + `$`)
|
||||
}
|
||||
191
vendor/github.com/docker/docker/LICENSE
generated
vendored
Normal file
191
vendor/github.com/docker/docker/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2013-2015 Docker, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
19
vendor/github.com/docker/docker/NOTICE
generated
vendored
Normal file
19
vendor/github.com/docker/docker/NOTICE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Docker
|
||||
Copyright 2012-2015 Docker, Inc.
|
||||
|
||||
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||
|
||||
This product contains software (https://github.com/kr/pty) developed
|
||||
by Keith Rarick, licensed under the MIT License.
|
||||
|
||||
The following is courtesy of our legal counsel:
|
||||
|
||||
|
||||
Use and transfer of Docker may be subject to certain restrictions by the
|
||||
United States and other governments.
|
||||
It is your responsibility to ensure that your use and/or transfer does not
|
||||
violate applicable laws.
|
||||
|
||||
For more information, please see https://www.bis.doc.gov
|
||||
|
||||
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
||||
69
vendor/github.com/docker/docker/pkg/mount/flags.go
generated
vendored
Normal file
69
vendor/github.com/docker/docker/pkg/mount/flags.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse fstab type mount options into mount() flags
|
||||
// and device specific data
|
||||
func parseOptions(options string) (int, string) {
|
||||
var (
|
||||
flag int
|
||||
data []string
|
||||
)
|
||||
|
||||
flags := map[string]struct {
|
||||
clear bool
|
||||
flag int
|
||||
}{
|
||||
"defaults": {false, 0},
|
||||
"ro": {false, RDONLY},
|
||||
"rw": {true, RDONLY},
|
||||
"suid": {true, NOSUID},
|
||||
"nosuid": {false, NOSUID},
|
||||
"dev": {true, NODEV},
|
||||
"nodev": {false, NODEV},
|
||||
"exec": {true, NOEXEC},
|
||||
"noexec": {false, NOEXEC},
|
||||
"sync": {false, SYNCHRONOUS},
|
||||
"async": {true, SYNCHRONOUS},
|
||||
"dirsync": {false, DIRSYNC},
|
||||
"remount": {false, REMOUNT},
|
||||
"mand": {false, MANDLOCK},
|
||||
"nomand": {true, MANDLOCK},
|
||||
"atime": {true, NOATIME},
|
||||
"noatime": {false, NOATIME},
|
||||
"diratime": {true, NODIRATIME},
|
||||
"nodiratime": {false, NODIRATIME},
|
||||
"bind": {false, BIND},
|
||||
"rbind": {false, RBIND},
|
||||
"unbindable": {false, UNBINDABLE},
|
||||
"runbindable": {false, RUNBINDABLE},
|
||||
"private": {false, PRIVATE},
|
||||
"rprivate": {false, RPRIVATE},
|
||||
"shared": {false, SHARED},
|
||||
"rshared": {false, RSHARED},
|
||||
"slave": {false, SLAVE},
|
||||
"rslave": {false, RSLAVE},
|
||||
"relatime": {false, RELATIME},
|
||||
"norelatime": {true, RELATIME},
|
||||
"strictatime": {false, STRICTATIME},
|
||||
"nostrictatime": {true, STRICTATIME},
|
||||
}
|
||||
|
||||
for _, o := range strings.Split(options, ",") {
|
||||
// If the option does not exist in the flags table or the flag
|
||||
// is not supported on the platform,
|
||||
// then it is a data value for a specific fs type
|
||||
if f, exists := flags[o]; exists && f.flag != 0 {
|
||||
if f.clear {
|
||||
flag &= ^f.flag
|
||||
} else {
|
||||
flag |= f.flag
|
||||
}
|
||||
} else {
|
||||
data = append(data, o)
|
||||
}
|
||||
}
|
||||
return flag, strings.Join(data, ",")
|
||||
}
|
||||
48
vendor/github.com/docker/docker/pkg/mount/flags_freebsd.go
generated
vendored
Normal file
48
vendor/github.com/docker/docker/pkg/mount/flags_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// +build freebsd,cgo
|
||||
|
||||
package mount
|
||||
|
||||
/*
|
||||
#include <sys/mount.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
// RDONLY will mount the filesystem as read-only.
|
||||
RDONLY = C.MNT_RDONLY
|
||||
|
||||
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
||||
// take effect.
|
||||
NOSUID = C.MNT_NOSUID
|
||||
|
||||
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
||||
NOEXEC = C.MNT_NOEXEC
|
||||
|
||||
// SYNCHRONOUS will allow any I/O to the file system to be done synchronously.
|
||||
SYNCHRONOUS = C.MNT_SYNCHRONOUS
|
||||
|
||||
// NOATIME will not update the file access time when reading from a file.
|
||||
NOATIME = C.MNT_NOATIME
|
||||
)
|
||||
|
||||
// These flags are unsupported.
|
||||
const (
|
||||
BIND = 0
|
||||
DIRSYNC = 0
|
||||
MANDLOCK = 0
|
||||
NODEV = 0
|
||||
NODIRATIME = 0
|
||||
UNBINDABLE = 0
|
||||
RUNBINDABLE = 0
|
||||
PRIVATE = 0
|
||||
RPRIVATE = 0
|
||||
SHARED = 0
|
||||
RSHARED = 0
|
||||
SLAVE = 0
|
||||
RSLAVE = 0
|
||||
RBIND = 0
|
||||
RELATIVE = 0
|
||||
RELATIME = 0
|
||||
REMOUNT = 0
|
||||
STRICTATIME = 0
|
||||
)
|
||||
85
vendor/github.com/docker/docker/pkg/mount/flags_linux.go
generated
vendored
Normal file
85
vendor/github.com/docker/docker/pkg/mount/flags_linux.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
// RDONLY will mount the file system read-only.
|
||||
RDONLY = syscall.MS_RDONLY
|
||||
|
||||
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
||||
// take effect.
|
||||
NOSUID = syscall.MS_NOSUID
|
||||
|
||||
// NODEV will not interpret character or block special devices on the file
|
||||
// system.
|
||||
NODEV = syscall.MS_NODEV
|
||||
|
||||
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
||||
NOEXEC = syscall.MS_NOEXEC
|
||||
|
||||
// SYNCHRONOUS will allow I/O to the file system to be done synchronously.
|
||||
SYNCHRONOUS = syscall.MS_SYNCHRONOUS
|
||||
|
||||
// DIRSYNC will force all directory updates within the file system to be done
|
||||
// synchronously. This affects the following system calls: creat, link,
|
||||
// unlink, symlink, mkdir, rmdir, mknod and rename.
|
||||
DIRSYNC = syscall.MS_DIRSYNC
|
||||
|
||||
// REMOUNT will attempt to remount an already-mounted file system. This is
|
||||
// commonly used to change the mount flags for a file system, especially to
|
||||
// make a readonly file system writeable. It does not change device or mount
|
||||
// point.
|
||||
REMOUNT = syscall.MS_REMOUNT
|
||||
|
||||
// MANDLOCK will force mandatory locks on a filesystem.
|
||||
MANDLOCK = syscall.MS_MANDLOCK
|
||||
|
||||
// NOATIME will not update the file access time when reading from a file.
|
||||
NOATIME = syscall.MS_NOATIME
|
||||
|
||||
// NODIRATIME will not update the directory access time.
|
||||
NODIRATIME = syscall.MS_NODIRATIME
|
||||
|
||||
// BIND remounts a subtree somewhere else.
|
||||
BIND = syscall.MS_BIND
|
||||
|
||||
// RBIND remounts a subtree and all possible submounts somewhere else.
|
||||
RBIND = syscall.MS_BIND | syscall.MS_REC
|
||||
|
||||
// UNBINDABLE creates a mount which cannot be cloned through a bind operation.
|
||||
UNBINDABLE = syscall.MS_UNBINDABLE
|
||||
|
||||
// RUNBINDABLE marks the entire mount tree as UNBINDABLE.
|
||||
RUNBINDABLE = syscall.MS_UNBINDABLE | syscall.MS_REC
|
||||
|
||||
// PRIVATE creates a mount which carries no propagation abilities.
|
||||
PRIVATE = syscall.MS_PRIVATE
|
||||
|
||||
// RPRIVATE marks the entire mount tree as PRIVATE.
|
||||
RPRIVATE = syscall.MS_PRIVATE | syscall.MS_REC
|
||||
|
||||
// SLAVE creates a mount which receives propagation from its master, but not
|
||||
// vice versa.
|
||||
SLAVE = syscall.MS_SLAVE
|
||||
|
||||
// RSLAVE marks the entire mount tree as SLAVE.
|
||||
RSLAVE = syscall.MS_SLAVE | syscall.MS_REC
|
||||
|
||||
// SHARED creates a mount which provides the ability to create mirrors of
|
||||
// that mount such that mounts and unmounts within any of the mirrors
|
||||
// propagate to the other mirrors.
|
||||
SHARED = syscall.MS_SHARED
|
||||
|
||||
// RSHARED marks the entire mount tree as SHARED.
|
||||
RSHARED = syscall.MS_SHARED | syscall.MS_REC
|
||||
|
||||
// RELATIME updates inode access times relative to modify or change time.
|
||||
RELATIME = syscall.MS_RELATIME
|
||||
|
||||
// STRICTATIME allows to explicitly request full atime updates. This makes
|
||||
// it possible for the kernel to default to relatime or noatime but still
|
||||
// allow userspace to override it.
|
||||
STRICTATIME = syscall.MS_STRICTATIME
|
||||
)
|
||||
30
vendor/github.com/docker/docker/pkg/mount/flags_unsupported.go
generated
vendored
Normal file
30
vendor/github.com/docker/docker/pkg/mount/flags_unsupported.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// +build !linux,!freebsd freebsd,!cgo
|
||||
|
||||
package mount
|
||||
|
||||
// These flags are unsupported.
|
||||
const (
|
||||
BIND = 0
|
||||
DIRSYNC = 0
|
||||
MANDLOCK = 0
|
||||
NOATIME = 0
|
||||
NODEV = 0
|
||||
NODIRATIME = 0
|
||||
NOEXEC = 0
|
||||
NOSUID = 0
|
||||
UNBINDABLE = 0
|
||||
RUNBINDABLE = 0
|
||||
PRIVATE = 0
|
||||
RPRIVATE = 0
|
||||
SHARED = 0
|
||||
RSHARED = 0
|
||||
SLAVE = 0
|
||||
RSLAVE = 0
|
||||
RBIND = 0
|
||||
RELATIME = 0
|
||||
RELATIVE = 0
|
||||
REMOUNT = 0
|
||||
STRICTATIME = 0
|
||||
SYNCHRONOUS = 0
|
||||
RDONLY = 0
|
||||
)
|
||||
74
vendor/github.com/docker/docker/pkg/mount/mount.go
generated
vendored
Normal file
74
vendor/github.com/docker/docker/pkg/mount/mount.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetMounts retrieves a list of mounts for the current running process.
|
||||
func GetMounts() ([]*Info, error) {
|
||||
return parseMountTable()
|
||||
}
|
||||
|
||||
// Mounted looks at /proc/self/mountinfo to determine of the specified
|
||||
// mountpoint has been mounted
|
||||
func Mounted(mountpoint string) (bool, error) {
|
||||
entries, err := parseMountTable()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Search the table for the mountpoint
|
||||
for _, e := range entries {
|
||||
if e.Mountpoint == mountpoint {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Mount will mount filesystem according to the specified configuration, on the
|
||||
// condition that the target path is *not* already mounted. Options must be
|
||||
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
||||
// flags.go for supported option flags.
|
||||
func Mount(device, target, mType, options string) error {
|
||||
flag, _ := parseOptions(options)
|
||||
if flag&REMOUNT != REMOUNT {
|
||||
if mounted, err := Mounted(target); err != nil || mounted {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return ForceMount(device, target, mType, options)
|
||||
}
|
||||
|
||||
// ForceMount will mount a filesystem according to the specified configuration,
|
||||
// *regardless* if the target path is not already mounted. Options must be
|
||||
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
||||
// flags.go for supported option flags.
|
||||
func ForceMount(device, target, mType, options string) error {
|
||||
flag, data := parseOptions(options)
|
||||
if err := mount(device, target, mType, uintptr(flag), data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmount will unmount the target filesystem, so long as it is mounted.
|
||||
func Unmount(target string) error {
|
||||
if mounted, err := Mounted(target); err != nil || !mounted {
|
||||
return err
|
||||
}
|
||||
return ForceUnmount(target)
|
||||
}
|
||||
|
||||
// ForceUnmount will force an unmount of the target filesystem, regardless if
|
||||
// it is mounted or not.
|
||||
func ForceUnmount(target string) (err error) {
|
||||
// Simple retry logic for unmount
|
||||
for i := 0; i < 10; i++ {
|
||||
if err = unmount(target, 0); err == nil {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
return
|
||||
}
|
||||
59
vendor/github.com/docker/docker/pkg/mount/mounter_freebsd.go
generated
vendored
Normal file
59
vendor/github.com/docker/docker/pkg/mount/mounter_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package mount
|
||||
|
||||
/*
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/_iovec.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/param.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func allocateIOVecs(options []string) []C.struct_iovec {
|
||||
out := make([]C.struct_iovec, len(options))
|
||||
for i, option := range options {
|
||||
out[i].iov_base = unsafe.Pointer(C.CString(option))
|
||||
out[i].iov_len = C.size_t(len(option) + 1)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mount(device, target, mType string, flag uintptr, data string) error {
|
||||
isNullFS := false
|
||||
|
||||
xs := strings.Split(data, ",")
|
||||
for _, x := range xs {
|
||||
if x == "bind" {
|
||||
isNullFS = true
|
||||
}
|
||||
}
|
||||
|
||||
options := []string{"fspath", target}
|
||||
if isNullFS {
|
||||
options = append(options, "fstype", "nullfs", "target", device)
|
||||
} else {
|
||||
options = append(options, "fstype", mType, "from", device)
|
||||
}
|
||||
rawOptions := allocateIOVecs(options)
|
||||
for _, rawOption := range rawOptions {
|
||||
defer C.free(rawOption.iov_base)
|
||||
}
|
||||
|
||||
if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 {
|
||||
reason := C.GoString(C.strerror(*C.__error()))
|
||||
return fmt.Errorf("Failed to call nmount: %s", reason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmount(target string, flag int) error {
|
||||
return syscall.Unmount(target, flag)
|
||||
}
|
||||
21
vendor/github.com/docker/docker/pkg/mount/mounter_linux.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/pkg/mount/mounter_linux.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func mount(device, target, mType string, flag uintptr, data string) error {
|
||||
if err := syscall.Mount(device, target, mType, flag, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we have a bind mount or remount, remount...
|
||||
if flag&syscall.MS_BIND == syscall.MS_BIND && flag&syscall.MS_RDONLY == syscall.MS_RDONLY {
|
||||
return syscall.Mount(device, target, mType, flag|syscall.MS_REMOUNT, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmount(target string, flag int) error {
|
||||
return syscall.Unmount(target, flag)
|
||||
}
|
||||
11
vendor/github.com/docker/docker/pkg/mount/mounter_unsupported.go
generated
vendored
Normal file
11
vendor/github.com/docker/docker/pkg/mount/mounter_unsupported.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build !linux,!freebsd freebsd,!cgo
|
||||
|
||||
package mount
|
||||
|
||||
func mount(device, target, mType string, flag uintptr, data string) error {
|
||||
panic("Not implemented")
|
||||
}
|
||||
|
||||
func unmount(target string, flag int) error {
|
||||
panic("Not implemented")
|
||||
}
|
||||
40
vendor/github.com/docker/docker/pkg/mount/mountinfo.go
generated
vendored
Normal file
40
vendor/github.com/docker/docker/pkg/mount/mountinfo.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package mount
|
||||
|
||||
// Info reveals information about a particular mounted filesystem. This
|
||||
// struct is populated from the content in the /proc/<pid>/mountinfo file.
|
||||
type Info struct {
|
||||
// ID is a unique identifier of the mount (may be reused after umount).
|
||||
ID int
|
||||
|
||||
// Parent indicates the ID of the mount parent (or of self for the top of the
|
||||
// mount tree).
|
||||
Parent int
|
||||
|
||||
// Major indicates one half of the device ID which identifies the device class.
|
||||
Major int
|
||||
|
||||
// Minor indicates one half of the device ID which identifies a specific
|
||||
// instance of device.
|
||||
Minor int
|
||||
|
||||
// Root of the mount within the filesystem.
|
||||
Root string
|
||||
|
||||
// Mountpoint indicates the mount point relative to the process's root.
|
||||
Mountpoint string
|
||||
|
||||
// Opts represents mount-specific options.
|
||||
Opts string
|
||||
|
||||
// Optional represents optional fields.
|
||||
Optional string
|
||||
|
||||
// Fstype indicates the type of filesystem, such as EXT3.
|
||||
Fstype string
|
||||
|
||||
// Source indicates filesystem specific information or "none".
|
||||
Source string
|
||||
|
||||
// VfsOpts represents per super block options.
|
||||
VfsOpts string
|
||||
}
|
||||
41
vendor/github.com/docker/docker/pkg/mount/mountinfo_freebsd.go
generated
vendored
Normal file
41
vendor/github.com/docker/docker/pkg/mount/mountinfo_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package mount
|
||||
|
||||
/*
|
||||
#include <sys/param.h>
|
||||
#include <sys/ucred.h>
|
||||
#include <sys/mount.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
|
||||
// bind mounts.
|
||||
func parseMountTable() ([]*Info, error) {
|
||||
var rawEntries *C.struct_statfs
|
||||
|
||||
count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT))
|
||||
if count == 0 {
|
||||
return nil, fmt.Errorf("Failed to call getmntinfo")
|
||||
}
|
||||
|
||||
var entries []C.struct_statfs
|
||||
header := (*reflect.SliceHeader)(unsafe.Pointer(&entries))
|
||||
header.Cap = count
|
||||
header.Len = count
|
||||
header.Data = uintptr(unsafe.Pointer(rawEntries))
|
||||
|
||||
var out []*Info
|
||||
for _, entry := range entries {
|
||||
var mountinfo Info
|
||||
mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0])
|
||||
mountinfo.Source = C.GoString(&entry.f_mntfromname[0])
|
||||
mountinfo.Fstype = C.GoString(&entry.f_fstypename[0])
|
||||
out = append(out, &mountinfo)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
95
vendor/github.com/docker/docker/pkg/mount/mountinfo_linux.go
generated
vendored
Normal file
95
vendor/github.com/docker/docker/pkg/mount/mountinfo_linux.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// +build linux
|
||||
|
||||
package mount
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
/* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
||||
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
|
||||
|
||||
(1) mount ID: unique identifier of the mount (may be reused after umount)
|
||||
(2) parent ID: ID of parent (or of self for the top of the mount tree)
|
||||
(3) major:minor: value of st_dev for files on filesystem
|
||||
(4) root: root of the mount within the filesystem
|
||||
(5) mount point: mount point relative to the process's root
|
||||
(6) mount options: per mount options
|
||||
(7) optional fields: zero or more fields of the form "tag[:value]"
|
||||
(8) separator: marks the end of the optional fields
|
||||
(9) filesystem type: name of filesystem of the form "type[.subtype]"
|
||||
(10) mount source: filesystem specific information or "none"
|
||||
(11) super options: per super block options*/
|
||||
mountinfoFormat = "%d %d %d:%d %s %s %s %s"
|
||||
)
|
||||
|
||||
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
|
||||
// bind mounts
|
||||
func parseMountTable() ([]*Info, error) {
|
||||
f, err := os.Open("/proc/self/mountinfo")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return parseInfoFile(f)
|
||||
}
|
||||
|
||||
func parseInfoFile(r io.Reader) ([]*Info, error) {
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
out = []*Info{}
|
||||
)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
p = &Info{}
|
||||
text = s.Text()
|
||||
optionalFields string
|
||||
)
|
||||
|
||||
if _, err := fmt.Sscanf(text, mountinfoFormat,
|
||||
&p.ID, &p.Parent, &p.Major, &p.Minor,
|
||||
&p.Root, &p.Mountpoint, &p.Opts, &optionalFields); err != nil {
|
||||
return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
|
||||
}
|
||||
// Safe as mountinfo encodes mountpoints with spaces as \040.
|
||||
index := strings.Index(text, " - ")
|
||||
postSeparatorFields := strings.Fields(text[index+3:])
|
||||
if len(postSeparatorFields) < 3 {
|
||||
return nil, fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
|
||||
}
|
||||
|
||||
if optionalFields != "-" {
|
||||
p.Optional = optionalFields
|
||||
}
|
||||
|
||||
p.Fstype = postSeparatorFields[0]
|
||||
p.Source = postSeparatorFields[1]
|
||||
p.VfsOpts = strings.Join(postSeparatorFields[2:], " ")
|
||||
out = append(out, p)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// PidMountInfo collects the mounts for a specific process ID. If the process
|
||||
// ID is unknown, it is better to use `GetMounts` which will inspect
|
||||
// "/proc/self/mountinfo" instead.
|
||||
func PidMountInfo(pid int) ([]*Info, error) {
|
||||
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return parseInfoFile(f)
|
||||
}
|
||||
12
vendor/github.com/docker/docker/pkg/mount/mountinfo_unsupported.go
generated
vendored
Normal file
12
vendor/github.com/docker/docker/pkg/mount/mountinfo_unsupported.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build !linux,!freebsd freebsd,!cgo
|
||||
|
||||
package mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func parseMountTable() ([]*Info, error) {
|
||||
return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
70
vendor/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go
generated
vendored
Normal file
70
vendor/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// +build linux
|
||||
|
||||
package mount
|
||||
|
||||
// MakeShared ensures a mounted filesystem has the SHARED mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakeShared(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "shared")
|
||||
}
|
||||
|
||||
// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakeRShared(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "rshared")
|
||||
}
|
||||
|
||||
// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakePrivate(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "private")
|
||||
}
|
||||
|
||||
// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option
|
||||
// enabled. See the supported options in flags.go for further reference.
|
||||
func MakeRPrivate(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "rprivate")
|
||||
}
|
||||
|
||||
// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakeSlave(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "slave")
|
||||
}
|
||||
|
||||
// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakeRSlave(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "rslave")
|
||||
}
|
||||
|
||||
// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option
|
||||
// enabled. See the supported options in flags.go for further reference.
|
||||
func MakeUnbindable(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "unbindable")
|
||||
}
|
||||
|
||||
// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount
|
||||
// option enabled. See the supported options in flags.go for further reference.
|
||||
func MakeRUnbindable(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "runbindable")
|
||||
}
|
||||
|
||||
func ensureMountedAs(mountPoint, options string) error {
|
||||
mounted, err := Mounted(mountPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !mounted {
|
||||
if err := Mount(mountPoint, mountPoint, "none", "bind,rw"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
mounted, err = Mounted(mountPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ForceMount("", mountPoint, "none", options)
|
||||
}
|
||||
67
vendor/github.com/docker/go-units/CONTRIBUTING.md
generated
vendored
Normal file
67
vendor/github.com/docker/go-units/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
# Contributing to go-units
|
||||
|
||||
Want to hack on go-units? Awesome! Here are instructions to get you started.
|
||||
|
||||
go-units is a part of the [Docker](https://www.docker.com) project, and follows
|
||||
the same rules and principles. If you're already familiar with the way
|
||||
Docker does things, you'll feel right at home.
|
||||
|
||||
Otherwise, go read Docker's
|
||||
[contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md),
|
||||
[issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md),
|
||||
[review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and
|
||||
[branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md).
|
||||
|
||||
### Sign your work
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the patch. Your
|
||||
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||
it on as an open-source patch. The rules are pretty simple: if you can certify
|
||||
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||
|
||||
```
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
|
||||
Then you just add a line to every git commit message:
|
||||
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
||||
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||
commit automatically with `git commit -s`.
|
||||
191
vendor/github.com/docker/go-units/LICENSE.code
generated
vendored
Normal file
191
vendor/github.com/docker/go-units/LICENSE.code
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2015 Docker, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
425
vendor/github.com/docker/go-units/LICENSE.docs
generated
vendored
Normal file
425
vendor/github.com/docker/go-units/LICENSE.docs
generated
vendored
Normal file
@@ -0,0 +1,425 @@
|
||||
Attribution-ShareAlike 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More_considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution-ShareAlike 4.0 International Public
|
||||
License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution-ShareAlike 4.0 International Public License ("Public
|
||||
License"). To the extent this Public License may be interpreted as a
|
||||
contract, You are granted the Licensed Rights in consideration of Your
|
||||
acceptance of these terms and conditions, and the Licensor grants You
|
||||
such rights in consideration of benefits the Licensor receives from
|
||||
making the Licensed Material available under these terms and
|
||||
conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. BY-SA Compatible License means a license listed at
|
||||
creativecommons.org/compatiblelicenses, approved by Creative
|
||||
Commons as essentially the equivalent of this Public License.
|
||||
|
||||
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
e. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
g. License Elements means the license attributes listed in the name
|
||||
of a Creative Commons Public License. The License Elements of this
|
||||
Public License are Attribution and ShareAlike.
|
||||
|
||||
h. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
i. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
k. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
l. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
m. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. Additional offer from the Licensor -- Adapted Material.
|
||||
Every recipient of Adapted Material from You
|
||||
automatically receives an offer from the Licensor to
|
||||
exercise the Licensed Rights in the Adapted Material
|
||||
under the conditions of the Adapter's License You apply.
|
||||
|
||||
c. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
b. ShareAlike.
|
||||
|
||||
In addition to the conditions in Section 3(a), if You Share
|
||||
Adapted Material You produce, the following conditions also apply.
|
||||
|
||||
1. The Adapter's License You apply must be a Creative Commons
|
||||
license with the same License Elements, this version or
|
||||
later, or a BY-SA Compatible License.
|
||||
|
||||
2. You must include the text of, or the URI or hyperlink to, the
|
||||
Adapter's License You apply. You may satisfy this condition
|
||||
in any reasonable manner based on the medium, means, and
|
||||
context in which You Share Adapted Material.
|
||||
|
||||
3. You may not offer or impose any additional or different terms
|
||||
or conditions on, or apply any Effective Technological
|
||||
Measures to, Adapted Material that restrict exercise of the
|
||||
rights granted under the Adapter's License You apply.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material,
|
||||
|
||||
including for purposes of Section 3(b); and
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public licenses.
|
||||
Notwithstanding, Creative Commons may elect to apply one of its public
|
||||
licenses to material it publishes and in those instances will be
|
||||
considered the "Licensor." Except for the limited purpose of indicating
|
||||
that material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the public
|
||||
licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
27
vendor/github.com/docker/go-units/MAINTAINERS
generated
vendored
Normal file
27
vendor/github.com/docker/go-units/MAINTAINERS
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# go-connections maintainers file
|
||||
#
|
||||
# This file describes who runs the docker/go-connections project and how.
|
||||
# This is a living document - if you see something out of date or missing, speak up!
|
||||
#
|
||||
# It is structured to be consumable by both humans and programs.
|
||||
# To extract its contents programmatically, use any TOML-compliant parser.
|
||||
#
|
||||
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
||||
#
|
||||
[Org]
|
||||
[Org."Core maintainers"]
|
||||
people = [
|
||||
"calavera",
|
||||
]
|
||||
|
||||
[people]
|
||||
|
||||
# A reference list of all people associated with the project.
|
||||
# All other sections should refer to people by their canonical key
|
||||
# in the people section.
|
||||
|
||||
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
||||
[people.calavera]
|
||||
Name = "David Calavera"
|
||||
Email = "david.calavera@gmail.com"
|
||||
GitHub = "calavera"
|
||||
18
vendor/github.com/docker/go-units/README.md
generated
vendored
Normal file
18
vendor/github.com/docker/go-units/README.md
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
[](https://godoc.org/github.com/docker/go-units)
|
||||
|
||||
# Introduction
|
||||
|
||||
go-units is a library to transform human friendly measurements into machine friendly values.
|
||||
|
||||
## Usage
|
||||
|
||||
See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation.
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Copyright © 2015 Docker, Inc. All rights reserved, except as follows. Code
|
||||
is released under the Apache 2.0 license. The README.md file, and files in the
|
||||
"docs" folder are licensed under the Creative Commons Attribution 4.0
|
||||
International License under the terms and conditions set forth in the file
|
||||
"LICENSE.docs". You may obtain a duplicate copy of the same license, titled
|
||||
CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/.
|
||||
11
vendor/github.com/docker/go-units/circle.yml
generated
vendored
Normal file
11
vendor/github.com/docker/go-units/circle.yml
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
dependencies:
|
||||
post:
|
||||
# install golint
|
||||
- go get github.com/golang/lint/golint
|
||||
|
||||
test:
|
||||
pre:
|
||||
# run analysis before tests
|
||||
- go vet ./...
|
||||
- test -z "$(golint ./... | tee /dev/stderr)"
|
||||
- test -z "$(gofmt -s -l . | tee /dev/stderr)"
|
||||
33
vendor/github.com/docker/go-units/duration.go
generated
vendored
Normal file
33
vendor/github.com/docker/go-units/duration.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Package units provides helper function to parse and print size and time units
|
||||
// in human-readable format.
|
||||
package units
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HumanDuration returns a human-readable approximation of a duration
|
||||
// (eg. "About a minute", "4 hours ago", etc.).
|
||||
func HumanDuration(d time.Duration) string {
|
||||
if seconds := int(d.Seconds()); seconds < 1 {
|
||||
return "Less than a second"
|
||||
} else if seconds < 60 {
|
||||
return fmt.Sprintf("%d seconds", seconds)
|
||||
} else if minutes := int(d.Minutes()); minutes == 1 {
|
||||
return "About a minute"
|
||||
} else if minutes < 60 {
|
||||
return fmt.Sprintf("%d minutes", minutes)
|
||||
} else if hours := int(d.Hours()); hours == 1 {
|
||||
return "About an hour"
|
||||
} else if hours < 48 {
|
||||
return fmt.Sprintf("%d hours", hours)
|
||||
} else if hours < 24*7*2 {
|
||||
return fmt.Sprintf("%d days", hours/24)
|
||||
} else if hours < 24*30*3 {
|
||||
return fmt.Sprintf("%d weeks", hours/24/7)
|
||||
} else if hours < 24*365*2 {
|
||||
return fmt.Sprintf("%d months", hours/24/30)
|
||||
}
|
||||
return fmt.Sprintf("%d years", int(d.Hours())/24/365)
|
||||
}
|
||||
95
vendor/github.com/docker/go-units/size.go
generated
vendored
Normal file
95
vendor/github.com/docker/go-units/size.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
package units
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// See: http://en.wikipedia.org/wiki/Binary_prefix
|
||||
const (
|
||||
// Decimal
|
||||
|
||||
KB = 1000
|
||||
MB = 1000 * KB
|
||||
GB = 1000 * MB
|
||||
TB = 1000 * GB
|
||||
PB = 1000 * TB
|
||||
|
||||
// Binary
|
||||
|
||||
KiB = 1024
|
||||
MiB = 1024 * KiB
|
||||
GiB = 1024 * MiB
|
||||
TiB = 1024 * GiB
|
||||
PiB = 1024 * TiB
|
||||
)
|
||||
|
||||
type unitMap map[string]int64
|
||||
|
||||
var (
|
||||
decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
|
||||
binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
|
||||
sizeRegex = regexp.MustCompile(`^(\d+)([kKmMgGtTpP])?[bB]?$`)
|
||||
)
|
||||
|
||||
var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||
var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
||||
|
||||
// CustomSize returns a human-readable approximation of a size
|
||||
// using custom format.
|
||||
func CustomSize(format string, size float64, base float64, _map []string) string {
|
||||
i := 0
|
||||
for size >= base {
|
||||
size = size / base
|
||||
i++
|
||||
}
|
||||
return fmt.Sprintf(format, size, _map[i])
|
||||
}
|
||||
|
||||
// HumanSize returns a human-readable approximation of a size
|
||||
// capped at 4 valid numbers (eg. "2.746 MB", "796 KB").
|
||||
func HumanSize(size float64) string {
|
||||
return CustomSize("%.4g %s", size, 1000.0, decimapAbbrs)
|
||||
}
|
||||
|
||||
// BytesSize returns a human-readable size in bytes, kibibytes,
|
||||
// mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB").
|
||||
func BytesSize(size float64) string {
|
||||
return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs)
|
||||
}
|
||||
|
||||
// FromHumanSize returns an integer from a human-readable specification of a
|
||||
// size using SI standard (eg. "44kB", "17MB").
|
||||
func FromHumanSize(size string) (int64, error) {
|
||||
return parseSize(size, decimalMap)
|
||||
}
|
||||
|
||||
// RAMInBytes parses a human-readable string representing an amount of RAM
|
||||
// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and
|
||||
// returns the number of bytes, or -1 if the string is unparseable.
|
||||
// Units are case-insensitive, and the 'b' suffix is optional.
|
||||
func RAMInBytes(size string) (int64, error) {
|
||||
return parseSize(size, binaryMap)
|
||||
}
|
||||
|
||||
// Parses the human-readable size string into the amount it represents.
|
||||
func parseSize(sizeStr string, uMap unitMap) (int64, error) {
|
||||
matches := sizeRegex.FindStringSubmatch(sizeStr)
|
||||
if len(matches) != 3 {
|
||||
return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
|
||||
}
|
||||
|
||||
size, err := strconv.ParseInt(matches[1], 10, 0)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
unitPrefix := strings.ToLower(matches[2])
|
||||
if mul, ok := uMap[unitPrefix]; ok {
|
||||
size *= mul
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
118
vendor/github.com/docker/go-units/ulimit.go
generated
vendored
Normal file
118
vendor/github.com/docker/go-units/ulimit.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
package units
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Ulimit is a human friendly version of Rlimit.
|
||||
type Ulimit struct {
|
||||
Name string
|
||||
Hard int64
|
||||
Soft int64
|
||||
}
|
||||
|
||||
// Rlimit specifies the resource limits, such as max open files.
|
||||
type Rlimit struct {
|
||||
Type int `json:"type,omitempty"`
|
||||
Hard uint64 `json:"hard,omitempty"`
|
||||
Soft uint64 `json:"soft,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
// magic numbers for making the syscall
|
||||
// some of these are defined in the syscall package, but not all.
|
||||
// Also since Windows client doesn't get access to the syscall package, need to
|
||||
// define these here
|
||||
rlimitAs = 9
|
||||
rlimitCore = 4
|
||||
rlimitCPU = 0
|
||||
rlimitData = 2
|
||||
rlimitFsize = 1
|
||||
rlimitLocks = 10
|
||||
rlimitMemlock = 8
|
||||
rlimitMsgqueue = 12
|
||||
rlimitNice = 13
|
||||
rlimitNofile = 7
|
||||
rlimitNproc = 6
|
||||
rlimitRss = 5
|
||||
rlimitRtprio = 14
|
||||
rlimitRttime = 15
|
||||
rlimitSigpending = 11
|
||||
rlimitStack = 3
|
||||
)
|
||||
|
||||
var ulimitNameMapping = map[string]int{
|
||||
//"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container.
|
||||
"core": rlimitCore,
|
||||
"cpu": rlimitCPU,
|
||||
"data": rlimitData,
|
||||
"fsize": rlimitFsize,
|
||||
"locks": rlimitLocks,
|
||||
"memlock": rlimitMemlock,
|
||||
"msgqueue": rlimitMsgqueue,
|
||||
"nice": rlimitNice,
|
||||
"nofile": rlimitNofile,
|
||||
"nproc": rlimitNproc,
|
||||
"rss": rlimitRss,
|
||||
"rtprio": rlimitRtprio,
|
||||
"rttime": rlimitRttime,
|
||||
"sigpending": rlimitSigpending,
|
||||
"stack": rlimitStack,
|
||||
}
|
||||
|
||||
// ParseUlimit parses and returns a Ulimit from the specified string.
|
||||
func ParseUlimit(val string) (*Ulimit, error) {
|
||||
parts := strings.SplitN(val, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("invalid ulimit argument: %s", val)
|
||||
}
|
||||
|
||||
if _, exists := ulimitNameMapping[parts[0]]; !exists {
|
||||
return nil, fmt.Errorf("invalid ulimit type: %s", parts[0])
|
||||
}
|
||||
|
||||
var (
|
||||
soft int64
|
||||
hard = &soft // default to soft in case no hard was set
|
||||
temp int64
|
||||
err error
|
||||
)
|
||||
switch limitVals := strings.Split(parts[1], ":"); len(limitVals) {
|
||||
case 2:
|
||||
temp, err = strconv.ParseInt(limitVals[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hard = &temp
|
||||
fallthrough
|
||||
case 1:
|
||||
soft, err = strconv.ParseInt(limitVals[0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1])
|
||||
}
|
||||
|
||||
if soft > *hard {
|
||||
return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard)
|
||||
}
|
||||
|
||||
return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil
|
||||
}
|
||||
|
||||
// GetRlimit returns the RLimit corresponding to Ulimit.
|
||||
func (u *Ulimit) GetRlimit() (*Rlimit, error) {
|
||||
t, exists := ulimitNameMapping[u.Name]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("invalid ulimit name %s", u.Name)
|
||||
}
|
||||
|
||||
return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil
|
||||
}
|
||||
|
||||
func (u *Ulimit) String() string {
|
||||
return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard)
|
||||
}
|
||||
70
vendor/github.com/emicklei/go-restful/.gitignore
generated
vendored
Normal file
70
vendor/github.com/emicklei/go-restful/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
restful.html
|
||||
|
||||
*.out
|
||||
|
||||
tmp.prof
|
||||
|
||||
go-restful.test
|
||||
|
||||
examples/restful-basic-authentication
|
||||
|
||||
examples/restful-encoding-filter
|
||||
|
||||
examples/restful-filters
|
||||
|
||||
examples/restful-hello-world
|
||||
|
||||
examples/restful-resource-functions
|
||||
|
||||
examples/restful-serve-static
|
||||
|
||||
examples/restful-user-service
|
||||
|
||||
*.DS_Store
|
||||
examples/restful-user-resource
|
||||
|
||||
examples/restful-multi-containers
|
||||
|
||||
examples/restful-form-handling
|
||||
|
||||
examples/restful-CORS-filter
|
||||
|
||||
examples/restful-options-filter
|
||||
|
||||
examples/restful-curly-router
|
||||
|
||||
examples/restful-cpuprofiler-service
|
||||
|
||||
examples/restful-pre-post-filters
|
||||
|
||||
curly.prof
|
||||
|
||||
examples/restful-NCSA-logging
|
||||
|
||||
examples/restful-html-template
|
||||
|
||||
s.html
|
||||
restful-path-tail
|
||||
163
vendor/github.com/emicklei/go-restful/CHANGES.md
generated
vendored
Normal file
163
vendor/github.com/emicklei/go-restful/CHANGES.md
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
Change history of go-restful
|
||||
=
|
||||
2016-02-14
|
||||
- take the qualify factor of the Accept header mediatype into account when deciding the contentype of the response
|
||||
- add constructors for custom entity accessors for xml and json
|
||||
|
||||
2015-09-27
|
||||
- rename new WriteStatusAnd... to WriteHeaderAnd... for consistency
|
||||
|
||||
2015-09-25
|
||||
- fixed problem with changing Header after WriteHeader (issue 235)
|
||||
|
||||
2015-09-14
|
||||
- changed behavior of WriteHeader (immediate write) and WriteEntity (no status write)
|
||||
- added support for custom EntityReaderWriters.
|
||||
|
||||
2015-08-06
|
||||
- add support for reading entities from compressed request content
|
||||
- use sync.Pool for compressors of http response and request body
|
||||
- add Description to Parameter for documentation in Swagger UI
|
||||
|
||||
2015-03-20
|
||||
- add configurable logging
|
||||
|
||||
2015-03-18
|
||||
- if not specified, the Operation is derived from the Route function
|
||||
|
||||
2015-03-17
|
||||
- expose Parameter creation functions
|
||||
- make trace logger an interface
|
||||
- fix OPTIONSFilter
|
||||
- customize rendering of ServiceError
|
||||
- JSR311 router now handles wildcards
|
||||
- add Notes to Route
|
||||
|
||||
2014-11-27
|
||||
- (api add) PrettyPrint per response. (as proposed in #167)
|
||||
|
||||
2014-11-12
|
||||
- (api add) ApiVersion(.) for documentation in Swagger UI
|
||||
|
||||
2014-11-10
|
||||
- (api change) struct fields tagged with "description" show up in Swagger UI
|
||||
|
||||
2014-10-31
|
||||
- (api change) ReturnsError -> Returns
|
||||
- (api add) RouteBuilder.Do(aBuilder) for DRY use of RouteBuilder
|
||||
- fix swagger nested structs
|
||||
- sort Swagger response messages by code
|
||||
|
||||
2014-10-23
|
||||
- (api add) ReturnsError allows you to document Http codes in swagger
|
||||
- fixed problem with greedy CurlyRouter
|
||||
- (api add) Access-Control-Max-Age in CORS
|
||||
- add tracing functionality (injectable) for debugging purposes
|
||||
- support JSON parse 64bit int
|
||||
- fix empty parameters for swagger
|
||||
- WebServicesUrl is now optional for swagger
|
||||
- fixed duplicate AccessControlAllowOrigin in CORS
|
||||
- (api change) expose ServeMux in container
|
||||
- (api add) added AllowedDomains in CORS
|
||||
- (api add) ParameterNamed for detailed documentation
|
||||
|
||||
2014-04-16
|
||||
- (api add) expose constructor of Request for testing.
|
||||
|
||||
2014-06-27
|
||||
- (api add) ParameterNamed gives access to a Parameter definition and its data (for further specification).
|
||||
- (api add) SetCacheReadEntity allow scontrol over whether or not the request body is being cached (default true for compatibility reasons).
|
||||
|
||||
2014-07-03
|
||||
- (api add) CORS can be configured with a list of allowed domains
|
||||
|
||||
2014-03-12
|
||||
- (api add) Route path parameters can use wildcard or regular expressions. (requires CurlyRouter)
|
||||
|
||||
2014-02-26
|
||||
- (api add) Request now provides information about the matched Route, see method SelectedRoutePath
|
||||
|
||||
2014-02-17
|
||||
- (api change) renamed parameter constants (go-lint checks)
|
||||
|
||||
2014-01-10
|
||||
- (api add) support for CloseNotify, see http://golang.org/pkg/net/http/#CloseNotifier
|
||||
|
||||
2014-01-07
|
||||
- (api change) Write* methods in Response now return the error or nil.
|
||||
- added example of serving HTML from a Go template.
|
||||
- fixed comparing Allowed headers in CORS (is now case-insensitive)
|
||||
|
||||
2013-11-13
|
||||
- (api add) Response knows how many bytes are written to the response body.
|
||||
|
||||
2013-10-29
|
||||
- (api add) RecoverHandler(handler RecoverHandleFunction) to change how panic recovery is handled. Default behavior is to log and return a stacktrace. This may be a security issue as it exposes sourcecode information.
|
||||
|
||||
2013-10-04
|
||||
- (api add) Response knows what HTTP status has been written
|
||||
- (api add) Request can have attributes (map of string->interface, also called request-scoped variables
|
||||
|
||||
2013-09-12
|
||||
- (api change) Router interface simplified
|
||||
- Implemented CurlyRouter, a Router that does not use|allow regular expressions in paths
|
||||
|
||||
2013-08-05
|
||||
- add OPTIONS support
|
||||
- add CORS support
|
||||
|
||||
2013-08-27
|
||||
- fixed some reported issues (see github)
|
||||
- (api change) deprecated use of WriteError; use WriteErrorString instead
|
||||
|
||||
2014-04-15
|
||||
- (fix) v1.0.1 tag: fix Issue 111: WriteErrorString
|
||||
|
||||
2013-08-08
|
||||
- (api add) Added implementation Container: a WebServices collection with its own http.ServeMux allowing multiple endpoints per program. Existing uses of go-restful will register their services to the DefaultContainer.
|
||||
- (api add) the swagger package has be extended to have a UI per container.
|
||||
- if panic is detected then a small stack trace is printed (thanks to runner-mei)
|
||||
- (api add) WriteErrorString to Response
|
||||
|
||||
Important API changes:
|
||||
|
||||
- (api remove) package variable DoNotRecover no longer works ; use restful.DefaultContainer.DoNotRecover(true) instead.
|
||||
- (api remove) package variable EnableContentEncoding no longer works ; use restful.DefaultContainer.EnableContentEncoding(true) instead.
|
||||
|
||||
|
||||
2013-07-06
|
||||
|
||||
- (api add) Added support for response encoding (gzip and deflate(zlib)). This feature is disabled on default (for backwards compatibility). Use restful.EnableContentEncoding = true in your initialization to enable this feature.
|
||||
|
||||
2013-06-19
|
||||
|
||||
- (improve) DoNotRecover option, moved request body closer, improved ReadEntity
|
||||
|
||||
2013-06-03
|
||||
|
||||
- (api change) removed Dispatcher interface, hide PathExpression
|
||||
- changed receiver names of type functions to be more idiomatic Go
|
||||
|
||||
2013-06-02
|
||||
|
||||
- (optimize) Cache the RegExp compilation of Paths.
|
||||
|
||||
2013-05-22
|
||||
|
||||
- (api add) Added support for request/response filter functions
|
||||
|
||||
2013-05-18
|
||||
|
||||
|
||||
- (api add) Added feature to change the default Http Request Dispatch function (travis cline)
|
||||
- (api change) Moved Swagger Webservice to swagger package (see example restful-user)
|
||||
|
||||
[2012-11-14 .. 2013-05-18>
|
||||
|
||||
- See https://github.com/emicklei/go-restful/commits
|
||||
|
||||
2012-11-14
|
||||
|
||||
- Initial commit
|
||||
|
||||
|
||||
22
vendor/github.com/emicklei/go-restful/LICENSE
generated
vendored
Normal file
22
vendor/github.com/emicklei/go-restful/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2012,2013 Ernest Micklei
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
74
vendor/github.com/emicklei/go-restful/README.md
generated
vendored
Normal file
74
vendor/github.com/emicklei/go-restful/README.md
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
go-restful
|
||||
==========
|
||||
|
||||
package for building REST-style Web Services using Google Go
|
||||
|
||||
REST asks developers to use HTTP methods explicitly and in a way that's consistent with the protocol definition. This basic REST design principle establishes a one-to-one mapping between create, read, update, and delete (CRUD) operations and HTTP methods. According to this mapping:
|
||||
|
||||
- GET = Retrieve a representation of a resource
|
||||
- POST = Create if you are sending content to the server to create a subordinate of the specified resource collection, using some server-side algorithm.
|
||||
- PUT = Create if you are sending the full content of the specified resource (URI).
|
||||
- PUT = Update if you are updating the full content of the specified resource.
|
||||
- DELETE = Delete if you are requesting the server to delete the resource
|
||||
- PATCH = Update partial content of a resource
|
||||
- OPTIONS = Get information about the communication options for the request URI
|
||||
|
||||
### Example
|
||||
|
||||
```Go
|
||||
ws := new(restful.WebService)
|
||||
ws.
|
||||
Path("/users").
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML)
|
||||
|
||||
ws.Route(ws.GET("/{user-id}").To(u.findUser).
|
||||
Doc("get a user").
|
||||
Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
|
||||
Writes(User{}))
|
||||
...
|
||||
|
||||
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
|
||||
id := request.PathParameter("user-id")
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
[Full API of a UserResource](https://github.com/emicklei/go-restful/tree/master/examples/restful-user-resource.go)
|
||||
|
||||
### Features
|
||||
|
||||
- Routes for request → function mapping with path parameter (e.g. {id}) support
|
||||
- Configurable router:
|
||||
- Routing algorithm after [JSR311](http://jsr311.java.net/nonav/releases/1.1/spec/spec.html) that is implemented using (but doest **not** accept) regular expressions (See RouterJSR311 which is used by default)
|
||||
- Fast routing algorithm that allows static elements, regular expressions and dynamic parameters in the URL path (e.g. /meetings/{id} or /static/{subpath:*}, See CurlyRouter)
|
||||
- Request API for reading structs from JSON/XML and accesing parameters (path,query,header)
|
||||
- Response API for writing structs to JSON/XML and setting headers
|
||||
- Filters for intercepting the request → response flow on Service or Route level
|
||||
- Request-scoped variables using attributes
|
||||
- Containers for WebServices on different HTTP endpoints
|
||||
- Content encoding (gzip,deflate) of request and response payloads
|
||||
- Automatic responses on OPTIONS (using a filter)
|
||||
- Automatic CORS request handling (using a filter)
|
||||
- API declaration for Swagger UI (see swagger package)
|
||||
- Panic recovery to produce HTTP 500, customizable using RecoverHandler(...)
|
||||
- Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...)
|
||||
- Configurable (trace) logging
|
||||
- Customizable encoding using EntityReaderWriter registration
|
||||
- Customizable gzip/deflate readers and writers using CompressorProvider registration
|
||||
|
||||
### Resources
|
||||
|
||||
- [Documentation on godoc.org](http://godoc.org/github.com/emicklei/go-restful)
|
||||
- [Code examples](https://github.com/emicklei/go-restful/tree/master/examples)
|
||||
- [Example posted on blog](http://ernestmicklei.com/2012/11/24/go-restful-first-working-example/)
|
||||
- [Design explained on blog](http://ernestmicklei.com/2012/11/11/go-restful-api-design/)
|
||||
- [sourcegraph](https://sourcegraph.com/github.com/emicklei/go-restful)
|
||||
- [gopkg.in](https://gopkg.in/emicklei/go-restful.v1)
|
||||
- [showcase: Mora - MongoDB REST Api server](https://github.com/emicklei/mora)
|
||||
|
||||
[](https://drone.io/github.com/emicklei/go-restful/latest)
|
||||
|
||||
(c) 2012 - 2015, http://ernestmicklei.com. MIT License
|
||||
|
||||
Type ```git shortlog -s``` for a full list of contributors.
|
||||
1
vendor/github.com/emicklei/go-restful/Srcfile
generated
vendored
Normal file
1
vendor/github.com/emicklei/go-restful/Srcfile
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"SkipDirs": ["examples"]}
|
||||
10
vendor/github.com/emicklei/go-restful/bench_test.sh
generated
vendored
Normal file
10
vendor/github.com/emicklei/go-restful/bench_test.sh
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
#go test -run=none -file bench_test.go -test.bench . -cpuprofile=bench_test.out
|
||||
|
||||
go test -c
|
||||
./go-restful.test -test.run=none -test.cpuprofile=tmp.prof -test.bench=BenchmarkMany
|
||||
./go-restful.test -test.run=none -test.cpuprofile=curly.prof -test.bench=BenchmarkManyCurly
|
||||
|
||||
#go tool pprof go-restful.test tmp.prof
|
||||
go tool pprof go-restful.test curly.prof
|
||||
|
||||
|
||||
123
vendor/github.com/emicklei/go-restful/compress.go
generated
vendored
Normal file
123
vendor/github.com/emicklei/go-restful/compress.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// OBSOLETE : use restful.DefaultContainer.EnableContentEncoding(true) to change this setting.
|
||||
var EnableContentEncoding = false
|
||||
|
||||
// CompressingResponseWriter is a http.ResponseWriter that can perform content encoding (gzip and zlib)
|
||||
type CompressingResponseWriter struct {
|
||||
writer http.ResponseWriter
|
||||
compressor io.WriteCloser
|
||||
encoding string
|
||||
}
|
||||
|
||||
// Header is part of http.ResponseWriter interface
|
||||
func (c *CompressingResponseWriter) Header() http.Header {
|
||||
return c.writer.Header()
|
||||
}
|
||||
|
||||
// WriteHeader is part of http.ResponseWriter interface
|
||||
func (c *CompressingResponseWriter) WriteHeader(status int) {
|
||||
c.writer.WriteHeader(status)
|
||||
}
|
||||
|
||||
// Write is part of http.ResponseWriter interface
|
||||
// It is passed through the compressor
|
||||
func (c *CompressingResponseWriter) Write(bytes []byte) (int, error) {
|
||||
if c.isCompressorClosed() {
|
||||
return -1, errors.New("Compressing error: tried to write data using closed compressor")
|
||||
}
|
||||
return c.compressor.Write(bytes)
|
||||
}
|
||||
|
||||
// CloseNotify is part of http.CloseNotifier interface
|
||||
func (c *CompressingResponseWriter) CloseNotify() <-chan bool {
|
||||
return c.writer.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// Close the underlying compressor
|
||||
func (c *CompressingResponseWriter) Close() error {
|
||||
if c.isCompressorClosed() {
|
||||
return errors.New("Compressing error: tried to close already closed compressor")
|
||||
}
|
||||
|
||||
c.compressor.Close()
|
||||
if ENCODING_GZIP == c.encoding {
|
||||
currentCompressorProvider.ReleaseGzipWriter(c.compressor.(*gzip.Writer))
|
||||
}
|
||||
if ENCODING_DEFLATE == c.encoding {
|
||||
currentCompressorProvider.ReleaseZlibWriter(c.compressor.(*zlib.Writer))
|
||||
}
|
||||
// gc hint needed?
|
||||
c.compressor = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CompressingResponseWriter) isCompressorClosed() bool {
|
||||
return nil == c.compressor
|
||||
}
|
||||
|
||||
// Hijack implements the Hijacker interface
|
||||
// This is especially useful when combining Container.EnabledContentEncoding
|
||||
// in combination with websockets (for instance gorilla/websocket)
|
||||
func (c *CompressingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
hijacker, ok := c.writer.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, errors.New("ResponseWriter doesn't support Hijacker interface")
|
||||
}
|
||||
return hijacker.Hijack()
|
||||
}
|
||||
|
||||
// WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
|
||||
func wantsCompressedResponse(httpRequest *http.Request) (bool, string) {
|
||||
header := httpRequest.Header.Get(HEADER_AcceptEncoding)
|
||||
gi := strings.Index(header, ENCODING_GZIP)
|
||||
zi := strings.Index(header, ENCODING_DEFLATE)
|
||||
// use in order of appearance
|
||||
if gi == -1 {
|
||||
return zi != -1, ENCODING_DEFLATE
|
||||
} else if zi == -1 {
|
||||
return gi != -1, ENCODING_GZIP
|
||||
} else {
|
||||
if gi < zi {
|
||||
return true, ENCODING_GZIP
|
||||
}
|
||||
return true, ENCODING_DEFLATE
|
||||
}
|
||||
}
|
||||
|
||||
// NewCompressingResponseWriter create a CompressingResponseWriter for a known encoding = {gzip,deflate}
|
||||
func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding string) (*CompressingResponseWriter, error) {
|
||||
httpWriter.Header().Set(HEADER_ContentEncoding, encoding)
|
||||
c := new(CompressingResponseWriter)
|
||||
c.writer = httpWriter
|
||||
var err error
|
||||
if ENCODING_GZIP == encoding {
|
||||
w := currentCompressorProvider.AcquireGzipWriter()
|
||||
w.Reset(httpWriter)
|
||||
c.compressor = w
|
||||
c.encoding = ENCODING_GZIP
|
||||
} else if ENCODING_DEFLATE == encoding {
|
||||
w := currentCompressorProvider.AcquireZlibWriter()
|
||||
w.Reset(httpWriter)
|
||||
c.compressor = w
|
||||
c.encoding = ENCODING_DEFLATE
|
||||
} else {
|
||||
return nil, errors.New("Unknown encoding:" + encoding)
|
||||
}
|
||||
return c, err
|
||||
}
|
||||
103
vendor/github.com/emicklei/go-restful/compressor_cache.go
generated
vendored
Normal file
103
vendor/github.com/emicklei/go-restful/compressor_cache.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
)
|
||||
|
||||
// BoundedCachedCompressors is a CompressorProvider that uses a cache with a fixed amount
|
||||
// of writers and readers (resources).
|
||||
// If a new resource is acquired and all are in use, it will return a new unmanaged resource.
|
||||
type BoundedCachedCompressors struct {
|
||||
gzipWriters chan *gzip.Writer
|
||||
gzipReaders chan *gzip.Reader
|
||||
zlibWriters chan *zlib.Writer
|
||||
writersCapacity int
|
||||
readersCapacity int
|
||||
}
|
||||
|
||||
// NewBoundedCachedCompressors returns a new, with filled cache, BoundedCachedCompressors.
|
||||
func NewBoundedCachedCompressors(writersCapacity, readersCapacity int) *BoundedCachedCompressors {
|
||||
b := &BoundedCachedCompressors{
|
||||
gzipWriters: make(chan *gzip.Writer, writersCapacity),
|
||||
gzipReaders: make(chan *gzip.Reader, readersCapacity),
|
||||
zlibWriters: make(chan *zlib.Writer, writersCapacity),
|
||||
writersCapacity: writersCapacity,
|
||||
readersCapacity: readersCapacity,
|
||||
}
|
||||
for ix := 0; ix < writersCapacity; ix++ {
|
||||
b.gzipWriters <- newGzipWriter()
|
||||
b.zlibWriters <- newZlibWriter()
|
||||
}
|
||||
for ix := 0; ix < readersCapacity; ix++ {
|
||||
b.gzipReaders <- newGzipReader()
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AcquireGzipWriter returns an resettable *gzip.Writer. Needs to be released.
|
||||
func (b *BoundedCachedCompressors) AcquireGzipWriter() *gzip.Writer {
|
||||
var writer *gzip.Writer
|
||||
select {
|
||||
case writer, _ = <-b.gzipWriters:
|
||||
default:
|
||||
// return a new unmanaged one
|
||||
writer = newGzipWriter()
|
||||
}
|
||||
return writer
|
||||
}
|
||||
|
||||
// ReleaseGzipWriter accepts a writer (does not have to be one that was cached)
|
||||
// only when the cache has room for it. It will ignore it otherwise.
|
||||
func (b *BoundedCachedCompressors) ReleaseGzipWriter(w *gzip.Writer) {
|
||||
// forget the unmanaged ones
|
||||
if len(b.gzipWriters) < b.writersCapacity {
|
||||
b.gzipWriters <- w
|
||||
}
|
||||
}
|
||||
|
||||
// AcquireGzipReader returns a *gzip.Reader. Needs to be released.
|
||||
func (b *BoundedCachedCompressors) AcquireGzipReader() *gzip.Reader {
|
||||
var reader *gzip.Reader
|
||||
select {
|
||||
case reader, _ = <-b.gzipReaders:
|
||||
default:
|
||||
// return a new unmanaged one
|
||||
reader = newGzipReader()
|
||||
}
|
||||
return reader
|
||||
}
|
||||
|
||||
// ReleaseGzipReader accepts a reader (does not have to be one that was cached)
|
||||
// only when the cache has room for it. It will ignore it otherwise.
|
||||
func (b *BoundedCachedCompressors) ReleaseGzipReader(r *gzip.Reader) {
|
||||
// forget the unmanaged ones
|
||||
if len(b.gzipReaders) < b.readersCapacity {
|
||||
b.gzipReaders <- r
|
||||
}
|
||||
}
|
||||
|
||||
// AcquireZlibWriter returns an resettable *zlib.Writer. Needs to be released.
|
||||
func (b *BoundedCachedCompressors) AcquireZlibWriter() *zlib.Writer {
|
||||
var writer *zlib.Writer
|
||||
select {
|
||||
case writer, _ = <-b.zlibWriters:
|
||||
default:
|
||||
// return a new unmanaged one
|
||||
writer = newZlibWriter()
|
||||
}
|
||||
return writer
|
||||
}
|
||||
|
||||
// ReleaseZlibWriter accepts a writer (does not have to be one that was cached)
|
||||
// only when the cache has room for it. It will ignore it otherwise.
|
||||
func (b *BoundedCachedCompressors) ReleaseZlibWriter(w *zlib.Writer) {
|
||||
// forget the unmanaged ones
|
||||
if len(b.zlibWriters) < b.writersCapacity {
|
||||
b.zlibWriters <- w
|
||||
}
|
||||
}
|
||||
91
vendor/github.com/emicklei/go-restful/compressor_pools.go
generated
vendored
Normal file
91
vendor/github.com/emicklei/go-restful/compressor_pools.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// SyncPoolCompessors is a CompressorProvider that use the standard sync.Pool.
|
||||
type SyncPoolCompessors struct {
|
||||
GzipWriterPool *sync.Pool
|
||||
GzipReaderPool *sync.Pool
|
||||
ZlibWriterPool *sync.Pool
|
||||
}
|
||||
|
||||
// NewSyncPoolCompessors returns a new ("empty") SyncPoolCompessors.
|
||||
func NewSyncPoolCompessors() *SyncPoolCompessors {
|
||||
return &SyncPoolCompessors{
|
||||
GzipWriterPool: &sync.Pool{
|
||||
New: func() interface{} { return newGzipWriter() },
|
||||
},
|
||||
GzipReaderPool: &sync.Pool{
|
||||
New: func() interface{} { return newGzipReader() },
|
||||
},
|
||||
ZlibWriterPool: &sync.Pool{
|
||||
New: func() interface{} { return newZlibWriter() },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SyncPoolCompessors) AcquireGzipWriter() *gzip.Writer {
|
||||
return s.GzipWriterPool.Get().(*gzip.Writer)
|
||||
}
|
||||
|
||||
func (s *SyncPoolCompessors) ReleaseGzipWriter(w *gzip.Writer) {
|
||||
s.GzipWriterPool.Put(w)
|
||||
}
|
||||
|
||||
func (s *SyncPoolCompessors) AcquireGzipReader() *gzip.Reader {
|
||||
return s.GzipReaderPool.Get().(*gzip.Reader)
|
||||
}
|
||||
|
||||
func (s *SyncPoolCompessors) ReleaseGzipReader(r *gzip.Reader) {
|
||||
s.GzipReaderPool.Put(r)
|
||||
}
|
||||
|
||||
func (s *SyncPoolCompessors) AcquireZlibWriter() *zlib.Writer {
|
||||
return s.ZlibWriterPool.Get().(*zlib.Writer)
|
||||
}
|
||||
|
||||
func (s *SyncPoolCompessors) ReleaseZlibWriter(w *zlib.Writer) {
|
||||
s.ZlibWriterPool.Put(w)
|
||||
}
|
||||
|
||||
func newGzipWriter() *gzip.Writer {
|
||||
// create with an empty bytes writer; it will be replaced before using the gzipWriter
|
||||
writer, err := gzip.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return writer
|
||||
}
|
||||
|
||||
func newGzipReader() *gzip.Reader {
|
||||
// create with an empty reader (but with GZIP header); it will be replaced before using the gzipReader
|
||||
// we can safely use currentCompressProvider because it is set on package initialization.
|
||||
w := currentCompressorProvider.AcquireGzipWriter()
|
||||
defer currentCompressorProvider.ReleaseGzipWriter(w)
|
||||
b := new(bytes.Buffer)
|
||||
w.Reset(b)
|
||||
w.Flush()
|
||||
w.Close()
|
||||
reader, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return reader
|
||||
}
|
||||
|
||||
func newZlibWriter() *zlib.Writer {
|
||||
writer, err := zlib.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return writer
|
||||
}
|
||||
53
vendor/github.com/emicklei/go-restful/compressors.go
generated
vendored
Normal file
53
vendor/github.com/emicklei/go-restful/compressors.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
)
|
||||
|
||||
type CompressorProvider interface {
|
||||
// Returns a *gzip.Writer which needs to be released later.
|
||||
// Before using it, call Reset().
|
||||
AcquireGzipWriter() *gzip.Writer
|
||||
|
||||
// Releases an aqcuired *gzip.Writer.
|
||||
ReleaseGzipWriter(w *gzip.Writer)
|
||||
|
||||
// Returns a *gzip.Reader which needs to be released later.
|
||||
AcquireGzipReader() *gzip.Reader
|
||||
|
||||
// Releases an aqcuired *gzip.Reader.
|
||||
ReleaseGzipReader(w *gzip.Reader)
|
||||
|
||||
// Returns a *zlib.Writer which needs to be released later.
|
||||
// Before using it, call Reset().
|
||||
AcquireZlibWriter() *zlib.Writer
|
||||
|
||||
// Releases an aqcuired *zlib.Writer.
|
||||
ReleaseZlibWriter(w *zlib.Writer)
|
||||
}
|
||||
|
||||
// DefaultCompressorProvider is the actual provider of compressors (zlib or gzip).
|
||||
var currentCompressorProvider CompressorProvider
|
||||
|
||||
func init() {
|
||||
currentCompressorProvider = NewSyncPoolCompessors()
|
||||
}
|
||||
|
||||
// CurrentCompressorProvider returns the current CompressorProvider.
|
||||
// It is initialized using a SyncPoolCompessors.
|
||||
func CurrentCompressorProvider() CompressorProvider {
|
||||
return currentCompressorProvider
|
||||
}
|
||||
|
||||
// CompressorProvider sets the actual provider of compressors (zlib or gzip).
|
||||
func SetCompressorProvider(p CompressorProvider) {
|
||||
if p == nil {
|
||||
panic("cannot set compressor provider to nil")
|
||||
}
|
||||
currentCompressorProvider = p
|
||||
}
|
||||
30
vendor/github.com/emicklei/go-restful/constants.go
generated
vendored
Normal file
30
vendor/github.com/emicklei/go-restful/constants.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
const (
|
||||
MIME_XML = "application/xml" // Accept or Content-Type used in Consumes() and/or Produces()
|
||||
MIME_JSON = "application/json" // Accept or Content-Type used in Consumes() and/or Produces()
|
||||
MIME_OCTET = "application/octet-stream" // If Content-Type is not present in request, use the default
|
||||
|
||||
HEADER_Allow = "Allow"
|
||||
HEADER_Accept = "Accept"
|
||||
HEADER_Origin = "Origin"
|
||||
HEADER_ContentType = "Content-Type"
|
||||
HEADER_LastModified = "Last-Modified"
|
||||
HEADER_AcceptEncoding = "Accept-Encoding"
|
||||
HEADER_ContentEncoding = "Content-Encoding"
|
||||
HEADER_AccessControlExposeHeaders = "Access-Control-Expose-Headers"
|
||||
HEADER_AccessControlRequestMethod = "Access-Control-Request-Method"
|
||||
HEADER_AccessControlRequestHeaders = "Access-Control-Request-Headers"
|
||||
HEADER_AccessControlAllowMethods = "Access-Control-Allow-Methods"
|
||||
HEADER_AccessControlAllowOrigin = "Access-Control-Allow-Origin"
|
||||
HEADER_AccessControlAllowCredentials = "Access-Control-Allow-Credentials"
|
||||
HEADER_AccessControlAllowHeaders = "Access-Control-Allow-Headers"
|
||||
HEADER_AccessControlMaxAge = "Access-Control-Max-Age"
|
||||
|
||||
ENCODING_GZIP = "gzip"
|
||||
ENCODING_DEFLATE = "deflate"
|
||||
)
|
||||
338
vendor/github.com/emicklei/go-restful/container.go
generated
vendored
Normal file
338
vendor/github.com/emicklei/go-restful/container.go
generated
vendored
Normal file
@@ -0,0 +1,338 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/emicklei/go-restful/log"
|
||||
)
|
||||
|
||||
// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
|
||||
// The requests are further dispatched to routes of WebServices using a RouteSelector
|
||||
type Container struct {
|
||||
webServicesLock sync.RWMutex
|
||||
webServices []*WebService
|
||||
ServeMux *http.ServeMux
|
||||
isRegisteredOnRoot bool
|
||||
containerFilters []FilterFunction
|
||||
doNotRecover bool // default is false
|
||||
recoverHandleFunc RecoverHandleFunction
|
||||
serviceErrorHandleFunc ServiceErrorHandleFunction
|
||||
router RouteSelector // default is a RouterJSR311, CurlyRouter is the faster alternative
|
||||
contentEncodingEnabled bool // default is false
|
||||
}
|
||||
|
||||
// NewContainer creates a new Container using a new ServeMux and default router (RouterJSR311)
|
||||
func NewContainer() *Container {
|
||||
return &Container{
|
||||
webServices: []*WebService{},
|
||||
ServeMux: http.NewServeMux(),
|
||||
isRegisteredOnRoot: false,
|
||||
containerFilters: []FilterFunction{},
|
||||
doNotRecover: false,
|
||||
recoverHandleFunc: logStackOnRecover,
|
||||
serviceErrorHandleFunc: writeServiceError,
|
||||
router: RouterJSR311{},
|
||||
contentEncodingEnabled: false}
|
||||
}
|
||||
|
||||
// RecoverHandleFunction declares functions that can be used to handle a panic situation.
|
||||
// The first argument is what recover() returns. The second must be used to communicate an error response.
|
||||
type RecoverHandleFunction func(interface{}, http.ResponseWriter)
|
||||
|
||||
// RecoverHandler changes the default function (logStackOnRecover) to be called
|
||||
// when a panic is detected. DoNotRecover must be have its default value (=false).
|
||||
func (c *Container) RecoverHandler(handler RecoverHandleFunction) {
|
||||
c.recoverHandleFunc = handler
|
||||
}
|
||||
|
||||
// ServiceErrorHandleFunction declares functions that can be used to handle a service error situation.
|
||||
// The first argument is the service error, the second is the request that resulted in the error and
|
||||
// the third must be used to communicate an error response.
|
||||
type ServiceErrorHandleFunction func(ServiceError, *Request, *Response)
|
||||
|
||||
// ServiceErrorHandler changes the default function (writeServiceError) to be called
|
||||
// when a ServiceError is detected.
|
||||
func (c *Container) ServiceErrorHandler(handler ServiceErrorHandleFunction) {
|
||||
c.serviceErrorHandleFunc = handler
|
||||
}
|
||||
|
||||
// DoNotRecover controls whether panics will be caught to return HTTP 500.
|
||||
// If set to true, Route functions are responsible for handling any error situation.
|
||||
// Default value is false = recover from panics. This has performance implications.
|
||||
func (c *Container) DoNotRecover(doNot bool) {
|
||||
c.doNotRecover = doNot
|
||||
}
|
||||
|
||||
// Router changes the default Router (currently RouterJSR311)
|
||||
func (c *Container) Router(aRouter RouteSelector) {
|
||||
c.router = aRouter
|
||||
}
|
||||
|
||||
// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses.
|
||||
func (c *Container) EnableContentEncoding(enabled bool) {
|
||||
c.contentEncodingEnabled = enabled
|
||||
}
|
||||
|
||||
// Add a WebService to the Container. It will detect duplicate root paths and panic in that case.
|
||||
func (c *Container) Add(service *WebService) *Container {
|
||||
c.webServicesLock.Lock()
|
||||
defer c.webServicesLock.Unlock()
|
||||
// If registered on root then no additional specific mapping is needed
|
||||
if !c.isRegisteredOnRoot {
|
||||
pattern := c.fixedPrefixPath(service.RootPath())
|
||||
// check if root path registration is needed
|
||||
if "/" == pattern || "" == pattern {
|
||||
c.ServeMux.HandleFunc("/", c.dispatch)
|
||||
c.isRegisteredOnRoot = true
|
||||
} else {
|
||||
// detect if registration already exists
|
||||
alreadyMapped := false
|
||||
for _, each := range c.webServices {
|
||||
if each.RootPath() == service.RootPath() {
|
||||
alreadyMapped = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !alreadyMapped {
|
||||
c.ServeMux.HandleFunc(pattern, c.dispatch)
|
||||
if !strings.HasSuffix(pattern, "/") {
|
||||
c.ServeMux.HandleFunc(pattern+"/", c.dispatch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// cannot have duplicate root paths
|
||||
for _, each := range c.webServices {
|
||||
if each.RootPath() == service.RootPath() {
|
||||
log.Printf("[restful] WebService with duplicate root path detected:['%v']", each)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
// if rootPath was not set then lazy initialize it
|
||||
if len(service.rootPath) == 0 {
|
||||
service.Path("/")
|
||||
}
|
||||
c.webServices = append(c.webServices, service)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Container) Remove(ws *WebService) error {
|
||||
c.webServicesLock.Lock()
|
||||
defer c.webServicesLock.Unlock()
|
||||
newServices := []*WebService{}
|
||||
for ix := range c.webServices {
|
||||
if c.webServices[ix].rootPath != ws.rootPath {
|
||||
newServices = append(newServices, c.webServices[ix])
|
||||
}
|
||||
}
|
||||
c.webServices = newServices
|
||||
return nil
|
||||
}
|
||||
|
||||
// logStackOnRecover is the default RecoverHandleFunction and is called
|
||||
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
|
||||
// Default implementation logs the stacktrace and writes the stacktrace on the response.
|
||||
// This may be a security issue as it exposes sourcecode information.
|
||||
func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(fmt.Sprintf("[restful] recover from panic situation: - %v\r\n", panicReason))
|
||||
for i := 2; ; i += 1 {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
|
||||
}
|
||||
log.Print(buffer.String())
|
||||
httpWriter.WriteHeader(http.StatusInternalServerError)
|
||||
httpWriter.Write(buffer.Bytes())
|
||||
}
|
||||
|
||||
// writeServiceError is the default ServiceErrorHandleFunction and is called
|
||||
// when a ServiceError is returned during route selection. Default implementation
|
||||
// calls resp.WriteErrorString(err.Code, err.Message)
|
||||
func writeServiceError(err ServiceError, req *Request, resp *Response) {
|
||||
resp.WriteErrorString(err.Code, err.Message)
|
||||
}
|
||||
|
||||
// Dispatch the incoming Http Request to a matching WebService.
|
||||
func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
|
||||
writer := httpWriter
|
||||
|
||||
// CompressingResponseWriter should be closed after all operations are done
|
||||
defer func() {
|
||||
if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
|
||||
compressWriter.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// Instal panic recovery unless told otherwise
|
||||
if !c.doNotRecover { // catch all for 500 response
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
c.recoverHandleFunc(r, writer)
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Install closing the request body (if any)
|
||||
defer func() {
|
||||
if nil != httpRequest.Body {
|
||||
httpRequest.Body.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// Detect if compression is needed
|
||||
// assume without compression, test for override
|
||||
if c.contentEncodingEnabled {
|
||||
doCompress, encoding := wantsCompressedResponse(httpRequest)
|
||||
if doCompress {
|
||||
var err error
|
||||
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
|
||||
if err != nil {
|
||||
log.Print("[restful] unable to install compressor: ", err)
|
||||
httpWriter.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// Find best match Route ; err is non nil if no match was found
|
||||
var webService *WebService
|
||||
var route *Route
|
||||
var err error
|
||||
func() {
|
||||
c.webServicesLock.RLock()
|
||||
defer c.webServicesLock.RUnlock()
|
||||
webService, route, err = c.router.SelectRoute(
|
||||
c.webServices,
|
||||
httpRequest)
|
||||
}()
|
||||
if err != nil {
|
||||
// a non-200 response has already been written
|
||||
// run container filters anyway ; they should not touch the response...
|
||||
chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
|
||||
switch err.(type) {
|
||||
case ServiceError:
|
||||
ser := err.(ServiceError)
|
||||
c.serviceErrorHandleFunc(ser, req, resp)
|
||||
}
|
||||
// TODO
|
||||
}}
|
||||
chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))
|
||||
return
|
||||
}
|
||||
wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest)
|
||||
// pass through filters (if any)
|
||||
if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
|
||||
// compose filter chain
|
||||
allFilters := []FilterFunction{}
|
||||
allFilters = append(allFilters, c.containerFilters...)
|
||||
allFilters = append(allFilters, webService.filters...)
|
||||
allFilters = append(allFilters, route.Filters...)
|
||||
chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) {
|
||||
// handle request by route after passing all filters
|
||||
route.Function(wrappedRequest, wrappedResponse)
|
||||
}}
|
||||
chain.ProcessFilter(wrappedRequest, wrappedResponse)
|
||||
} else {
|
||||
// no filters, handle request by route
|
||||
route.Function(wrappedRequest, wrappedResponse)
|
||||
}
|
||||
}
|
||||
|
||||
// fixedPrefixPath returns the fixed part of the partspec ; it may include template vars {}
|
||||
func (c Container) fixedPrefixPath(pathspec string) string {
|
||||
varBegin := strings.Index(pathspec, "{")
|
||||
if -1 == varBegin {
|
||||
return pathspec
|
||||
}
|
||||
return pathspec[:varBegin]
|
||||
}
|
||||
|
||||
// ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server
|
||||
func (c Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {
|
||||
c.ServeMux.ServeHTTP(httpwriter, httpRequest)
|
||||
}
|
||||
|
||||
// Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
|
||||
func (c Container) Handle(pattern string, handler http.Handler) {
|
||||
c.ServeMux.Handle(pattern, handler)
|
||||
}
|
||||
|
||||
// HandleWithFilter registers the handler for the given pattern.
|
||||
// Container's filter chain is applied for handler.
|
||||
// If a handler already exists for pattern, HandleWithFilter panics.
|
||||
func (c *Container) HandleWithFilter(pattern string, handler http.Handler) {
|
||||
f := func(httpResponse http.ResponseWriter, httpRequest *http.Request) {
|
||||
if len(c.containerFilters) == 0 {
|
||||
handler.ServeHTTP(httpResponse, httpRequest)
|
||||
return
|
||||
}
|
||||
|
||||
chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
|
||||
handler.ServeHTTP(httpResponse, httpRequest)
|
||||
}}
|
||||
chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse))
|
||||
}
|
||||
|
||||
c.Handle(pattern, http.HandlerFunc(f))
|
||||
}
|
||||
|
||||
// Filter appends a container FilterFunction. These are called before dispatching
|
||||
// a http.Request to a WebService from the container
|
||||
func (c *Container) Filter(filter FilterFunction) {
|
||||
c.containerFilters = append(c.containerFilters, filter)
|
||||
}
|
||||
|
||||
// RegisteredWebServices returns the collections of added WebServices
|
||||
func (c Container) RegisteredWebServices() []*WebService {
|
||||
c.webServicesLock.RLock()
|
||||
defer c.webServicesLock.RUnlock()
|
||||
result := make([]*WebService, len(c.webServices))
|
||||
for ix := range c.webServices {
|
||||
result[ix] = c.webServices[ix]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
|
||||
func (c Container) computeAllowedMethods(req *Request) []string {
|
||||
// Go through all RegisteredWebServices() and all its Routes to collect the options
|
||||
methods := []string{}
|
||||
requestPath := req.Request.URL.Path
|
||||
for _, ws := range c.RegisteredWebServices() {
|
||||
matches := ws.pathExpr.Matcher.FindStringSubmatch(requestPath)
|
||||
if matches != nil {
|
||||
finalMatch := matches[len(matches)-1]
|
||||
for _, rt := range ws.Routes() {
|
||||
matches := rt.pathExpr.Matcher.FindStringSubmatch(finalMatch)
|
||||
if matches != nil {
|
||||
lastMatch := matches[len(matches)-1]
|
||||
if lastMatch == "" || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
|
||||
methods = append(methods, rt.Method)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// methods = append(methods, "OPTIONS") not sure about this
|
||||
return methods
|
||||
}
|
||||
|
||||
// newBasicRequestResponse creates a pair of Request,Response from its http versions.
|
||||
// It is basic because no parameter or (produces) content-type information is given.
|
||||
func newBasicRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
|
||||
resp := NewResponse(httpWriter)
|
||||
resp.requestAccept = httpRequest.Header.Get(HEADER_Accept)
|
||||
return NewRequest(httpRequest), resp
|
||||
}
|
||||
176
vendor/github.com/emicklei/go-restful/cors_filter.go
generated
vendored
Normal file
176
vendor/github.com/emicklei/go-restful/cors_filter.go
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CrossOriginResourceSharing is used to create a Container Filter that implements CORS.
|
||||
// Cross-origin resource sharing (CORS) is a mechanism that allows JavaScript on a web page
|
||||
// to make XMLHttpRequests to another domain, not the domain the JavaScript originated from.
|
||||
//
|
||||
// http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
|
||||
// http://enable-cors.org/server.html
|
||||
// http://www.html5rocks.com/en/tutorials/cors/#toc-handling-a-not-so-simple-request
|
||||
type CrossOriginResourceSharing struct {
|
||||
ExposeHeaders []string // list of Header names
|
||||
AllowedHeaders []string // list of Header names
|
||||
AllowedDomains []string // list of allowed values for Http Origin. If empty all are allowed.
|
||||
AllowedMethods []string
|
||||
MaxAge int // number of seconds before requiring new Options request
|
||||
CookiesAllowed bool
|
||||
Container *Container
|
||||
}
|
||||
|
||||
// Filter is a filter function that implements the CORS flow as documented on http://enable-cors.org/server.html
|
||||
// and http://www.html5rocks.com/static/images/cors_server_flowchart.png
|
||||
func (c CrossOriginResourceSharing) Filter(req *Request, resp *Response, chain *FilterChain) {
|
||||
origin := req.Request.Header.Get(HEADER_Origin)
|
||||
if len(origin) == 0 {
|
||||
if trace {
|
||||
traceLogger.Print("no Http header Origin set")
|
||||
}
|
||||
chain.ProcessFilter(req, resp)
|
||||
return
|
||||
}
|
||||
if len(c.AllowedDomains) > 0 { // if provided then origin must be included
|
||||
included := false
|
||||
for _, each := range c.AllowedDomains {
|
||||
if each == origin {
|
||||
included = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !included {
|
||||
if trace {
|
||||
traceLogger.Printf("HTTP Origin:%s is not part of %v", origin, c.AllowedDomains)
|
||||
}
|
||||
chain.ProcessFilter(req, resp)
|
||||
return
|
||||
}
|
||||
}
|
||||
if req.Request.Method != "OPTIONS" {
|
||||
c.doActualRequest(req, resp)
|
||||
chain.ProcessFilter(req, resp)
|
||||
return
|
||||
}
|
||||
if acrm := req.Request.Header.Get(HEADER_AccessControlRequestMethod); acrm != "" {
|
||||
c.doPreflightRequest(req, resp)
|
||||
} else {
|
||||
c.doActualRequest(req, resp)
|
||||
chain.ProcessFilter(req, resp)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c CrossOriginResourceSharing) doActualRequest(req *Request, resp *Response) {
|
||||
c.setOptionsHeaders(req, resp)
|
||||
// continue processing the response
|
||||
}
|
||||
|
||||
func (c *CrossOriginResourceSharing) doPreflightRequest(req *Request, resp *Response) {
|
||||
if len(c.AllowedMethods) == 0 {
|
||||
if c.Container == nil {
|
||||
c.AllowedMethods = DefaultContainer.computeAllowedMethods(req)
|
||||
} else {
|
||||
c.AllowedMethods = c.Container.computeAllowedMethods(req)
|
||||
}
|
||||
}
|
||||
|
||||
acrm := req.Request.Header.Get(HEADER_AccessControlRequestMethod)
|
||||
if !c.isValidAccessControlRequestMethod(acrm, c.AllowedMethods) {
|
||||
if trace {
|
||||
traceLogger.Printf("Http header %s:%s is not in %v",
|
||||
HEADER_AccessControlRequestMethod,
|
||||
acrm,
|
||||
c.AllowedMethods)
|
||||
}
|
||||
return
|
||||
}
|
||||
acrhs := req.Request.Header.Get(HEADER_AccessControlRequestHeaders)
|
||||
if len(acrhs) > 0 {
|
||||
for _, each := range strings.Split(acrhs, ",") {
|
||||
if !c.isValidAccessControlRequestHeader(strings.Trim(each, " ")) {
|
||||
if trace {
|
||||
traceLogger.Printf("Http header %s:%s is not in %v",
|
||||
HEADER_AccessControlRequestHeaders,
|
||||
acrhs,
|
||||
c.AllowedHeaders)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
resp.AddHeader(HEADER_AccessControlAllowMethods, strings.Join(c.AllowedMethods, ","))
|
||||
resp.AddHeader(HEADER_AccessControlAllowHeaders, acrhs)
|
||||
c.setOptionsHeaders(req, resp)
|
||||
|
||||
// return http 200 response, no body
|
||||
}
|
||||
|
||||
func (c CrossOriginResourceSharing) setOptionsHeaders(req *Request, resp *Response) {
|
||||
c.checkAndSetExposeHeaders(resp)
|
||||
c.setAllowOriginHeader(req, resp)
|
||||
c.checkAndSetAllowCredentials(resp)
|
||||
if c.MaxAge > 0 {
|
||||
resp.AddHeader(HEADER_AccessControlMaxAge, strconv.Itoa(c.MaxAge))
|
||||
}
|
||||
}
|
||||
|
||||
func (c CrossOriginResourceSharing) isOriginAllowed(origin string) bool {
|
||||
if len(origin) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(c.AllowedDomains) == 0 {
|
||||
return true
|
||||
}
|
||||
allowed := false
|
||||
for _, each := range c.AllowedDomains {
|
||||
if each == origin {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return allowed
|
||||
}
|
||||
|
||||
func (c CrossOriginResourceSharing) setAllowOriginHeader(req *Request, resp *Response) {
|
||||
origin := req.Request.Header.Get(HEADER_Origin)
|
||||
if c.isOriginAllowed(origin) {
|
||||
resp.AddHeader(HEADER_AccessControlAllowOrigin, origin)
|
||||
}
|
||||
}
|
||||
|
||||
func (c CrossOriginResourceSharing) checkAndSetExposeHeaders(resp *Response) {
|
||||
if len(c.ExposeHeaders) > 0 {
|
||||
resp.AddHeader(HEADER_AccessControlExposeHeaders, strings.Join(c.ExposeHeaders, ","))
|
||||
}
|
||||
}
|
||||
|
||||
func (c CrossOriginResourceSharing) checkAndSetAllowCredentials(resp *Response) {
|
||||
if c.CookiesAllowed {
|
||||
resp.AddHeader(HEADER_AccessControlAllowCredentials, "true")
|
||||
}
|
||||
}
|
||||
|
||||
func (c CrossOriginResourceSharing) isValidAccessControlRequestMethod(method string, allowedMethods []string) bool {
|
||||
for _, each := range allowedMethods {
|
||||
if each == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header string) bool {
|
||||
for _, each := range c.AllowedHeaders {
|
||||
if strings.ToLower(each) == strings.ToLower(header) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
2
vendor/github.com/emicklei/go-restful/coverage.sh
generated
vendored
Normal file
2
vendor/github.com/emicklei/go-restful/coverage.sh
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test -coverprofile=coverage.out
|
||||
go tool cover -html=coverage.out
|
||||
162
vendor/github.com/emicklei/go-restful/curly.go
generated
vendored
Normal file
162
vendor/github.com/emicklei/go-restful/curly.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CurlyRouter expects Routes with paths that contain zero or more parameters in curly brackets.
|
||||
type CurlyRouter struct{}
|
||||
|
||||
// SelectRoute is part of the Router interface and returns the best match
|
||||
// for the WebService and its Route for the given Request.
|
||||
func (c CurlyRouter) SelectRoute(
|
||||
webServices []*WebService,
|
||||
httpRequest *http.Request) (selectedService *WebService, selected *Route, err error) {
|
||||
|
||||
requestTokens := tokenizePath(httpRequest.URL.Path)
|
||||
|
||||
detectedService := c.detectWebService(requestTokens, webServices)
|
||||
if detectedService == nil {
|
||||
if trace {
|
||||
traceLogger.Printf("no WebService was found to match URL path:%s\n", httpRequest.URL.Path)
|
||||
}
|
||||
return nil, nil, NewError(http.StatusNotFound, "404: Page Not Found")
|
||||
}
|
||||
candidateRoutes := c.selectRoutes(detectedService, requestTokens)
|
||||
if len(candidateRoutes) == 0 {
|
||||
if trace {
|
||||
traceLogger.Printf("no Route in WebService with path %s was found to match URL path:%s\n", detectedService.rootPath, httpRequest.URL.Path)
|
||||
}
|
||||
return detectedService, nil, NewError(http.StatusNotFound, "404: Page Not Found")
|
||||
}
|
||||
selectedRoute, err := c.detectRoute(candidateRoutes, httpRequest)
|
||||
if selectedRoute == nil {
|
||||
return detectedService, nil, err
|
||||
}
|
||||
return detectedService, selectedRoute, nil
|
||||
}
|
||||
|
||||
// selectRoutes return a collection of Route from a WebService that matches the path tokens from the request.
|
||||
func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) []Route {
|
||||
candidates := &sortableCurlyRoutes{[]*curlyRoute{}}
|
||||
for _, each := range ws.routes {
|
||||
matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens)
|
||||
if matches {
|
||||
candidates.add(&curlyRoute{each, paramCount, staticCount}) // TODO make sure Routes() return pointers?
|
||||
}
|
||||
}
|
||||
sort.Sort(sort.Reverse(candidates))
|
||||
return candidates.routes()
|
||||
}
|
||||
|
||||
// matchesRouteByPathTokens computes whether it matches, howmany parameters do match and what the number of static path elements are.
|
||||
func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []string) (matches bool, paramCount int, staticCount int) {
|
||||
if len(routeTokens) < len(requestTokens) {
|
||||
// proceed in matching only if last routeToken is wildcard
|
||||
count := len(routeTokens)
|
||||
if count == 0 || !strings.HasSuffix(routeTokens[count-1], "*}") {
|
||||
return false, 0, 0
|
||||
}
|
||||
// proceed
|
||||
}
|
||||
for i, routeToken := range routeTokens {
|
||||
if i == len(requestTokens) {
|
||||
// reached end of request path
|
||||
return false, 0, 0
|
||||
}
|
||||
requestToken := requestTokens[i]
|
||||
if strings.HasPrefix(routeToken, "{") {
|
||||
paramCount++
|
||||
if colon := strings.Index(routeToken, ":"); colon != -1 {
|
||||
// match by regex
|
||||
matchesToken, matchesRemainder := c.regularMatchesPathToken(routeToken, colon, requestToken)
|
||||
if !matchesToken {
|
||||
return false, 0, 0
|
||||
}
|
||||
if matchesRemainder {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else { // no { prefix
|
||||
if requestToken != routeToken {
|
||||
return false, 0, 0
|
||||
}
|
||||
staticCount++
|
||||
}
|
||||
}
|
||||
return true, paramCount, staticCount
|
||||
}
|
||||
|
||||
// regularMatchesPathToken tests whether the regular expression part of routeToken matches the requestToken or all remaining tokens
|
||||
// format routeToken is {someVar:someExpression}, e.g. {zipcode:[\d][\d][\d][\d][A-Z][A-Z]}
|
||||
func (c CurlyRouter) regularMatchesPathToken(routeToken string, colon int, requestToken string) (matchesToken bool, matchesRemainder bool) {
|
||||
regPart := routeToken[colon+1 : len(routeToken)-1]
|
||||
if regPart == "*" {
|
||||
if trace {
|
||||
traceLogger.Printf("wildcard parameter detected in route token %s that matches %s\n", routeToken, requestToken)
|
||||
}
|
||||
return true, true
|
||||
}
|
||||
matched, err := regexp.MatchString(regPart, requestToken)
|
||||
return (matched && err == nil), false
|
||||
}
|
||||
|
||||
// detectRoute selectes from a list of Route the first match by inspecting both the Accept and Content-Type
|
||||
// headers of the Request. See also RouterJSR311 in jsr311.go
|
||||
func (c CurlyRouter) detectRoute(candidateRoutes []Route, httpRequest *http.Request) (*Route, error) {
|
||||
// tracing is done inside detectRoute
|
||||
return RouterJSR311{}.detectRoute(candidateRoutes, httpRequest)
|
||||
}
|
||||
|
||||
// detectWebService returns the best matching webService given the list of path tokens.
|
||||
// see also computeWebserviceScore
|
||||
func (c CurlyRouter) detectWebService(requestTokens []string, webServices []*WebService) *WebService {
|
||||
var best *WebService
|
||||
score := -1
|
||||
for _, each := range webServices {
|
||||
matches, eachScore := c.computeWebserviceScore(requestTokens, each.pathExpr.tokens)
|
||||
if matches && (eachScore > score) {
|
||||
best = each
|
||||
score = eachScore
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
// computeWebserviceScore returns whether tokens match and
|
||||
// the weighted score of the longest matching consecutive tokens from the beginning.
|
||||
func (c CurlyRouter) computeWebserviceScore(requestTokens []string, tokens []string) (bool, int) {
|
||||
if len(tokens) > len(requestTokens) {
|
||||
return false, 0
|
||||
}
|
||||
score := 0
|
||||
for i := 0; i < len(tokens); i++ {
|
||||
each := requestTokens[i]
|
||||
other := tokens[i]
|
||||
if len(each) == 0 && len(other) == 0 {
|
||||
score++
|
||||
continue
|
||||
}
|
||||
if len(other) > 0 && strings.HasPrefix(other, "{") {
|
||||
// no empty match
|
||||
if len(each) == 0 {
|
||||
return false, score
|
||||
}
|
||||
score += 1
|
||||
} else {
|
||||
// not a parameter
|
||||
if each != other {
|
||||
return false, score
|
||||
}
|
||||
score += (len(tokens) - i) * 10 //fuzzy
|
||||
}
|
||||
}
|
||||
return true, score
|
||||
}
|
||||
54
vendor/github.com/emicklei/go-restful/curly_route.go
generated
vendored
Normal file
54
vendor/github.com/emicklei/go-restful/curly_route.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// curlyRoute exits for sorting Routes by the CurlyRouter based on number of parameters and number of static path elements.
|
||||
type curlyRoute struct {
|
||||
route Route
|
||||
paramCount int
|
||||
staticCount int
|
||||
}
|
||||
|
||||
type sortableCurlyRoutes struct {
|
||||
candidates []*curlyRoute
|
||||
}
|
||||
|
||||
func (s *sortableCurlyRoutes) add(route *curlyRoute) {
|
||||
s.candidates = append(s.candidates, route)
|
||||
}
|
||||
|
||||
func (s *sortableCurlyRoutes) routes() (routes []Route) {
|
||||
for _, each := range s.candidates {
|
||||
routes = append(routes, each.route) // TODO change return type
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
func (s *sortableCurlyRoutes) Len() int {
|
||||
return len(s.candidates)
|
||||
}
|
||||
func (s *sortableCurlyRoutes) Swap(i, j int) {
|
||||
s.candidates[i], s.candidates[j] = s.candidates[j], s.candidates[i]
|
||||
}
|
||||
func (s *sortableCurlyRoutes) Less(i, j int) bool {
|
||||
ci := s.candidates[i]
|
||||
cj := s.candidates[j]
|
||||
|
||||
// primary key
|
||||
if ci.staticCount < cj.staticCount {
|
||||
return true
|
||||
}
|
||||
if ci.staticCount > cj.staticCount {
|
||||
return false
|
||||
}
|
||||
// secundary key
|
||||
if ci.paramCount < cj.paramCount {
|
||||
return true
|
||||
}
|
||||
if ci.paramCount > cj.paramCount {
|
||||
return false
|
||||
}
|
||||
return ci.route.Path < cj.route.Path
|
||||
}
|
||||
196
vendor/github.com/emicklei/go-restful/doc.go
generated
vendored
Normal file
196
vendor/github.com/emicklei/go-restful/doc.go
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
Package restful, a lean package for creating REST-style WebServices without magic.
|
||||
|
||||
WebServices and Routes
|
||||
|
||||
A WebService has a collection of Route objects that dispatch incoming Http Requests to a function calls.
|
||||
Typically, a WebService has a root path (e.g. /users) and defines common MIME types for its routes.
|
||||
WebServices must be added to a container (see below) in order to handler Http requests from a server.
|
||||
|
||||
A Route is defined by a HTTP method, an URL path and (optionally) the MIME types it consumes (Content-Type) and produces (Accept).
|
||||
This package has the logic to find the best matching Route and if found, call its Function.
|
||||
|
||||
ws := new(restful.WebService)
|
||||
ws.
|
||||
Path("/users").
|
||||
Consumes(restful.MIME_JSON, restful.MIME_XML).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML)
|
||||
|
||||
ws.Route(ws.GET("/{user-id}").To(u.findUser)) // u is a UserResource
|
||||
|
||||
...
|
||||
|
||||
// GET http://localhost:8080/users/1
|
||||
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
|
||||
id := request.PathParameter("user-id")
|
||||
...
|
||||
}
|
||||
|
||||
The (*Request, *Response) arguments provide functions for reading information from the request and writing information back to the response.
|
||||
|
||||
See the example https://github.com/emicklei/go-restful/blob/master/examples/restful-user-resource.go with a full implementation.
|
||||
|
||||
Regular expression matching Routes
|
||||
|
||||
A Route parameter can be specified using the format "uri/{var[:regexp]}" or the special version "uri/{var:*}" for matching the tail of the path.
|
||||
For example, /persons/{name:[A-Z][A-Z]} can be used to restrict values for the parameter "name" to only contain capital alphabetic characters.
|
||||
Regular expressions must use the standard Go syntax as described in the regexp package. (https://code.google.com/p/re2/wiki/Syntax)
|
||||
This feature requires the use of a CurlyRouter.
|
||||
|
||||
Containers
|
||||
|
||||
A Container holds a collection of WebServices, Filters and a http.ServeMux for multiplexing http requests.
|
||||
Using the statements "restful.Add(...) and restful.Filter(...)" will register WebServices and Filters to the Default Container.
|
||||
The Default container of go-restful uses the http.DefaultServeMux.
|
||||
You can create your own Container and create a new http.Server for that particular container.
|
||||
|
||||
container := restful.NewContainer()
|
||||
server := &http.Server{Addr: ":8081", Handler: container}
|
||||
|
||||
Filters
|
||||
|
||||
A filter dynamically intercepts requests and responses to transform or use the information contained in the requests or responses.
|
||||
You can use filters to perform generic logging, measurement, authentication, redirect, set response headers etc.
|
||||
In the restful package there are three hooks into the request,response flow where filters can be added.
|
||||
Each filter must define a FilterFunction:
|
||||
|
||||
func (req *restful.Request, resp *restful.Response, chain *restful.FilterChain)
|
||||
|
||||
Use the following statement to pass the request,response pair to the next filter or RouteFunction
|
||||
|
||||
chain.ProcessFilter(req, resp)
|
||||
|
||||
Container Filters
|
||||
|
||||
These are processed before any registered WebService.
|
||||
|
||||
// install a (global) filter for the default container (processed before any webservice)
|
||||
restful.Filter(globalLogging)
|
||||
|
||||
WebService Filters
|
||||
|
||||
These are processed before any Route of a WebService.
|
||||
|
||||
// install a webservice filter (processed before any route)
|
||||
ws.Filter(webserviceLogging).Filter(measureTime)
|
||||
|
||||
|
||||
Route Filters
|
||||
|
||||
These are processed before calling the function associated with the Route.
|
||||
|
||||
// install 2 chained route filters (processed before calling findUser)
|
||||
ws.Route(ws.GET("/{user-id}").Filter(routeLogging).Filter(NewCountFilter().routeCounter).To(findUser))
|
||||
|
||||
See the example https://github.com/emicklei/go-restful/blob/master/examples/restful-filters.go with full implementations.
|
||||
|
||||
Response Encoding
|
||||
|
||||
Two encodings are supported: gzip and deflate. To enable this for all responses:
|
||||
|
||||
restful.DefaultContainer.EnableContentEncoding(true)
|
||||
|
||||
If a Http request includes the Accept-Encoding header then the response content will be compressed using the specified encoding.
|
||||
Alternatively, you can create a Filter that performs the encoding and install it per WebService or Route.
|
||||
|
||||
See the example https://github.com/emicklei/go-restful/blob/master/examples/restful-encoding-filter.go
|
||||
|
||||
OPTIONS support
|
||||
|
||||
By installing a pre-defined container filter, your Webservice(s) can respond to the OPTIONS Http request.
|
||||
|
||||
Filter(OPTIONSFilter())
|
||||
|
||||
CORS
|
||||
|
||||
By installing the filter of a CrossOriginResourceSharing (CORS), your WebService(s) can handle CORS requests.
|
||||
|
||||
cors := CrossOriginResourceSharing{ExposeHeaders: []string{"X-My-Header"}, CookiesAllowed: false, Container: DefaultContainer}
|
||||
Filter(cors.Filter)
|
||||
|
||||
Error Handling
|
||||
|
||||
Unexpected things happen. If a request cannot be processed because of a failure, your service needs to tell via the response what happened and why.
|
||||
For this reason HTTP status codes exist and it is important to use the correct code in every exceptional situation.
|
||||
|
||||
400: Bad Request
|
||||
|
||||
If path or query parameters are not valid (content or type) then use http.StatusBadRequest.
|
||||
|
||||
404: Not Found
|
||||
|
||||
Despite a valid URI, the resource requested may not be available
|
||||
|
||||
500: Internal Server Error
|
||||
|
||||
If the application logic could not process the request (or write the response) then use http.StatusInternalServerError.
|
||||
|
||||
405: Method Not Allowed
|
||||
|
||||
The request has a valid URL but the method (GET,PUT,POST,...) is not allowed.
|
||||
|
||||
406: Not Acceptable
|
||||
|
||||
The request does not have or has an unknown Accept Header set for this operation.
|
||||
|
||||
415: Unsupported Media Type
|
||||
|
||||
The request does not have or has an unknown Content-Type Header set for this operation.
|
||||
|
||||
ServiceError
|
||||
|
||||
In addition to setting the correct (error) Http status code, you can choose to write a ServiceError message on the response.
|
||||
|
||||
Performance options
|
||||
|
||||
This package has several options that affect the performance of your service. It is important to understand them and how you can change it.
|
||||
|
||||
restful.DefaultContainer.Router(CurlyRouter{})
|
||||
|
||||
The default router is the RouterJSR311 which is an implementation of its spec (http://jsr311.java.net/nonav/releases/1.1/spec/spec.html).
|
||||
However, it uses regular expressions for all its routes which, depending on your usecase, may consume a significant amount of time.
|
||||
The CurlyRouter implementation is more lightweight that also allows you to use wildcards and expressions, but only if needed.
|
||||
|
||||
restful.DefaultContainer.DoNotRecover(true)
|
||||
|
||||
DoNotRecover controls whether panics will be caught to return HTTP 500.
|
||||
If set to true, Route functions are responsible for handling any error situation.
|
||||
Default value is false; it will recover from panics. This has performance implications.
|
||||
|
||||
restful.SetCacheReadEntity(false)
|
||||
|
||||
SetCacheReadEntity controls whether the response data ([]byte) is cached such that ReadEntity is repeatable.
|
||||
If you expect to read large amounts of payload data, and you do not use this feature, you should set it to false.
|
||||
|
||||
restful.SetCompressorProvider(NewBoundedCachedCompressors(20, 20))
|
||||
|
||||
If content encoding is enabled then the default strategy for getting new gzip/zlib writers and readers is to use a sync.Pool.
|
||||
Because writers are expensive structures, performance is even more improved when using a preloaded cache. You can also inject your own implementation.
|
||||
|
||||
Trouble shooting
|
||||
|
||||
This package has the means to produce detail logging of the complete Http request matching process and filter invocation.
|
||||
Enabling this feature requires you to set an implementation of restful.StdLogger (e.g. log.Logger) instance such as:
|
||||
|
||||
restful.TraceLogger(log.New(os.Stdout, "[restful] ", log.LstdFlags|log.Lshortfile))
|
||||
|
||||
Logging
|
||||
|
||||
The restful.SetLogger() method allows you to override the logger used by the package. By default restful
|
||||
uses the standard library `log` package and logs to stdout. Different logging packages are supported as
|
||||
long as they conform to `StdLogger` interface defined in the `log` sub-package, writing an adapter for your
|
||||
preferred package is simple.
|
||||
|
||||
Resources
|
||||
|
||||
[project]: https://github.com/emicklei/go-restful
|
||||
|
||||
[examples]: https://github.com/emicklei/go-restful/blob/master/examples
|
||||
|
||||
[design]: http://ernestmicklei.com/2012/11/11/go-restful-api-design/
|
||||
|
||||
[showcases]: https://github.com/emicklei/mora, https://github.com/emicklei/landskape
|
||||
|
||||
(c) 2012-2015, http://ernestmicklei.com. MIT License
|
||||
*/
|
||||
package restful
|
||||
163
vendor/github.com/emicklei/go-restful/entity_accessors.go
generated
vendored
Normal file
163
vendor/github.com/emicklei/go-restful/entity_accessors.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// EntityReaderWriter can read and write values using an encoding such as JSON,XML.
|
||||
type EntityReaderWriter interface {
|
||||
// Read a serialized version of the value from the request.
|
||||
// The Request may have a decompressing reader. Depends on Content-Encoding.
|
||||
Read(req *Request, v interface{}) error
|
||||
|
||||
// Write a serialized version of the value on the response.
|
||||
// The Response may have a compressing writer. Depends on Accept-Encoding.
|
||||
// status should be a valid Http Status code
|
||||
Write(resp *Response, status int, v interface{}) error
|
||||
}
|
||||
|
||||
// entityAccessRegistry is a singleton
|
||||
var entityAccessRegistry = &entityReaderWriters{
|
||||
protection: new(sync.RWMutex),
|
||||
accessors: map[string]EntityReaderWriter{},
|
||||
}
|
||||
|
||||
// entityReaderWriters associates MIME to an EntityReaderWriter
|
||||
type entityReaderWriters struct {
|
||||
protection *sync.RWMutex
|
||||
accessors map[string]EntityReaderWriter
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterEntityAccessor(MIME_JSON, NewEntityAccessorJSON(MIME_JSON))
|
||||
RegisterEntityAccessor(MIME_XML, NewEntityAccessorXML(MIME_XML))
|
||||
}
|
||||
|
||||
// RegisterEntityAccessor add/overrides the ReaderWriter for encoding content with this MIME type.
|
||||
func RegisterEntityAccessor(mime string, erw EntityReaderWriter) {
|
||||
entityAccessRegistry.protection.Lock()
|
||||
defer entityAccessRegistry.protection.Unlock()
|
||||
entityAccessRegistry.accessors[mime] = erw
|
||||
}
|
||||
|
||||
// NewEntityAccessorJSON returns a new EntityReaderWriter for accessing JSON content.
|
||||
// This package is already initialized with such an accessor using the MIME_JSON contentType.
|
||||
func NewEntityAccessorJSON(contentType string) EntityReaderWriter {
|
||||
return entityJSONAccess{ContentType: contentType}
|
||||
}
|
||||
|
||||
// NewEntityAccessorXML returns a new EntityReaderWriter for accessing XML content.
|
||||
// This package is already initialized with such an accessor using the MIME_XML contentType.
|
||||
func NewEntityAccessorXML(contentType string) EntityReaderWriter {
|
||||
return entityXMLAccess{ContentType: contentType}
|
||||
}
|
||||
|
||||
// accessorAt returns the registered ReaderWriter for this MIME type.
|
||||
func (r *entityReaderWriters) accessorAt(mime string) (EntityReaderWriter, bool) {
|
||||
r.protection.RLock()
|
||||
defer r.protection.RUnlock()
|
||||
er, ok := r.accessors[mime]
|
||||
if !ok {
|
||||
// retry with reverse lookup
|
||||
// more expensive but we are in an exceptional situation anyway
|
||||
for k, v := range r.accessors {
|
||||
if strings.Contains(mime, k) {
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return er, ok
|
||||
}
|
||||
|
||||
// entityXMLAccess is a EntityReaderWriter for XML encoding
|
||||
type entityXMLAccess struct {
|
||||
// This is used for setting the Content-Type header when writing
|
||||
ContentType string
|
||||
}
|
||||
|
||||
// Read unmarshalls the value from XML
|
||||
func (e entityXMLAccess) Read(req *Request, v interface{}) error {
|
||||
return xml.NewDecoder(req.Request.Body).Decode(v)
|
||||
}
|
||||
|
||||
// Write marshalls the value to JSON and set the Content-Type Header.
|
||||
func (e entityXMLAccess) Write(resp *Response, status int, v interface{}) error {
|
||||
return writeXML(resp, status, e.ContentType, v)
|
||||
}
|
||||
|
||||
// writeXML marshalls the value to JSON and set the Content-Type Header.
|
||||
func writeXML(resp *Response, status int, contentType string, v interface{}) error {
|
||||
if v == nil {
|
||||
resp.WriteHeader(status)
|
||||
// do not write a nil representation
|
||||
return nil
|
||||
}
|
||||
if resp.prettyPrint {
|
||||
// pretty output must be created and written explicitly
|
||||
output, err := xml.MarshalIndent(v, " ", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Header().Set(HEADER_ContentType, contentType)
|
||||
resp.WriteHeader(status)
|
||||
_, err = resp.Write([]byte(xml.Header))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = resp.Write(output)
|
||||
return err
|
||||
}
|
||||
// not-so-pretty
|
||||
resp.Header().Set(HEADER_ContentType, contentType)
|
||||
resp.WriteHeader(status)
|
||||
return xml.NewEncoder(resp).Encode(v)
|
||||
}
|
||||
|
||||
// entityJSONAccess is a EntityReaderWriter for JSON encoding
|
||||
type entityJSONAccess struct {
|
||||
// This is used for setting the Content-Type header when writing
|
||||
ContentType string
|
||||
}
|
||||
|
||||
// Read unmarshalls the value from JSON
|
||||
func (e entityJSONAccess) Read(req *Request, v interface{}) error {
|
||||
decoder := json.NewDecoder(req.Request.Body)
|
||||
decoder.UseNumber()
|
||||
return decoder.Decode(v)
|
||||
}
|
||||
|
||||
// Write marshalls the value to JSON and set the Content-Type Header.
|
||||
func (e entityJSONAccess) Write(resp *Response, status int, v interface{}) error {
|
||||
return writeJSON(resp, status, e.ContentType, v)
|
||||
}
|
||||
|
||||
// write marshalls the value to JSON and set the Content-Type Header.
|
||||
func writeJSON(resp *Response, status int, contentType string, v interface{}) error {
|
||||
if v == nil {
|
||||
resp.WriteHeader(status)
|
||||
// do not write a nil representation
|
||||
return nil
|
||||
}
|
||||
if resp.prettyPrint {
|
||||
// pretty output must be created and written explicitly
|
||||
output, err := json.MarshalIndent(v, " ", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Header().Set(HEADER_ContentType, contentType)
|
||||
resp.WriteHeader(status)
|
||||
_, err = resp.Write(output)
|
||||
return err
|
||||
}
|
||||
// not-so-pretty
|
||||
resp.Header().Set(HEADER_ContentType, contentType)
|
||||
resp.WriteHeader(status)
|
||||
return json.NewEncoder(resp).Encode(v)
|
||||
}
|
||||
26
vendor/github.com/emicklei/go-restful/filter.go
generated
vendored
Normal file
26
vendor/github.com/emicklei/go-restful/filter.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// FilterChain is a request scoped object to process one or more filters before calling the target RouteFunction.
|
||||
type FilterChain struct {
|
||||
Filters []FilterFunction // ordered list of FilterFunction
|
||||
Index int // index into filters that is currently in progress
|
||||
Target RouteFunction // function to call after passing all filters
|
||||
}
|
||||
|
||||
// ProcessFilter passes the request,response pair through the next of Filters.
|
||||
// Each filter can decide to proceed to the next Filter or handle the Response itself.
|
||||
func (f *FilterChain) ProcessFilter(request *Request, response *Response) {
|
||||
if f.Index < len(f.Filters) {
|
||||
f.Index++
|
||||
f.Filters[f.Index-1](request, response, f)
|
||||
} else {
|
||||
f.Target(request, response)
|
||||
}
|
||||
}
|
||||
|
||||
// FilterFunction definitions must call ProcessFilter on the FilterChain to pass on the control and eventually call the RouteFunction
|
||||
type FilterFunction func(*Request, *Response, *FilterChain)
|
||||
9
vendor/github.com/emicklei/go-restful/install.sh
generated
vendored
Normal file
9
vendor/github.com/emicklei/go-restful/install.sh
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
cd examples
|
||||
ls *.go | xargs -I {} go build -o /tmp/ignore {}
|
||||
cd ..
|
||||
go fmt ...swagger && \
|
||||
go test -test.v ...swagger && \
|
||||
go install ...swagger && \
|
||||
go fmt ...restful && \
|
||||
go test -test.v ...restful && \
|
||||
go install ...restful
|
||||
247
vendor/github.com/emicklei/go-restful/jsr311.go
generated
vendored
Normal file
247
vendor/github.com/emicklei/go-restful/jsr311.go
generated
vendored
Normal file
@@ -0,0 +1,247 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// RouterJSR311 implements the flow for matching Requests to Routes (and consequently Resource Functions)
|
||||
// as specified by the JSR311 http://jsr311.java.net/nonav/releases/1.1/spec/spec.html.
|
||||
// RouterJSR311 implements the Router interface.
|
||||
// Concept of locators is not implemented.
|
||||
type RouterJSR311 struct{}
|
||||
|
||||
// SelectRoute is part of the Router interface and returns the best match
|
||||
// for the WebService and its Route for the given Request.
|
||||
func (r RouterJSR311) SelectRoute(
|
||||
webServices []*WebService,
|
||||
httpRequest *http.Request) (selectedService *WebService, selectedRoute *Route, err error) {
|
||||
|
||||
// Identify the root resource class (WebService)
|
||||
dispatcher, finalMatch, err := r.detectDispatcher(httpRequest.URL.Path, webServices)
|
||||
if err != nil {
|
||||
return nil, nil, NewError(http.StatusNotFound, "")
|
||||
}
|
||||
// Obtain the set of candidate methods (Routes)
|
||||
routes := r.selectRoutes(dispatcher, finalMatch)
|
||||
if len(routes) == 0 {
|
||||
return dispatcher, nil, NewError(http.StatusNotFound, "404: Page Not Found")
|
||||
}
|
||||
|
||||
// Identify the method (Route) that will handle the request
|
||||
route, ok := r.detectRoute(routes, httpRequest)
|
||||
return dispatcher, route, ok
|
||||
}
|
||||
|
||||
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
|
||||
func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) {
|
||||
// http method
|
||||
methodOk := []Route{}
|
||||
for _, each := range routes {
|
||||
if httpRequest.Method == each.Method {
|
||||
methodOk = append(methodOk, each)
|
||||
}
|
||||
}
|
||||
if len(methodOk) == 0 {
|
||||
if trace {
|
||||
traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(routes), httpRequest.Method)
|
||||
}
|
||||
return nil, NewError(http.StatusMethodNotAllowed, "405: Method Not Allowed")
|
||||
}
|
||||
inputMediaOk := methodOk
|
||||
|
||||
// content-type
|
||||
contentType := httpRequest.Header.Get(HEADER_ContentType)
|
||||
inputMediaOk = []Route{}
|
||||
for _, each := range methodOk {
|
||||
if each.matchesContentType(contentType) {
|
||||
inputMediaOk = append(inputMediaOk, each)
|
||||
}
|
||||
}
|
||||
if len(inputMediaOk) == 0 {
|
||||
if trace {
|
||||
traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(methodOk), contentType)
|
||||
}
|
||||
return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
|
||||
}
|
||||
|
||||
// accept
|
||||
outputMediaOk := []Route{}
|
||||
accept := httpRequest.Header.Get(HEADER_Accept)
|
||||
if accept == "" {
|
||||
accept = "*/*"
|
||||
}
|
||||
for _, each := range inputMediaOk {
|
||||
if each.matchesAccept(accept) {
|
||||
outputMediaOk = append(outputMediaOk, each)
|
||||
}
|
||||
}
|
||||
if len(outputMediaOk) == 0 {
|
||||
if trace {
|
||||
traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(inputMediaOk), accept)
|
||||
}
|
||||
return nil, NewError(http.StatusNotAcceptable, "406: Not Acceptable")
|
||||
}
|
||||
return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil
|
||||
}
|
||||
|
||||
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
|
||||
// n/m > n/* > */*
|
||||
func (r RouterJSR311) bestMatchByMedia(routes []Route, contentType string, accept string) *Route {
|
||||
// TODO
|
||||
return &routes[0]
|
||||
}
|
||||
|
||||
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 2)
|
||||
func (r RouterJSR311) selectRoutes(dispatcher *WebService, pathRemainder string) []Route {
|
||||
filtered := &sortableRouteCandidates{}
|
||||
for _, each := range dispatcher.Routes() {
|
||||
pathExpr := each.pathExpr
|
||||
matches := pathExpr.Matcher.FindStringSubmatch(pathRemainder)
|
||||
if matches != nil {
|
||||
lastMatch := matches[len(matches)-1]
|
||||
if len(lastMatch) == 0 || lastMatch == "/" { // do not include if value is neither empty nor ‘/’.
|
||||
filtered.candidates = append(filtered.candidates,
|
||||
routeCandidate{each, len(matches) - 1, pathExpr.LiteralCount, pathExpr.VarCount})
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(filtered.candidates) == 0 {
|
||||
if trace {
|
||||
traceLogger.Printf("WebService on path %s has no routes that match URL path remainder:%s\n", dispatcher.rootPath, pathRemainder)
|
||||
}
|
||||
return []Route{}
|
||||
}
|
||||
sort.Sort(sort.Reverse(filtered))
|
||||
|
||||
// select other routes from candidates whoes expression matches rmatch
|
||||
matchingRoutes := []Route{filtered.candidates[0].route}
|
||||
for c := 1; c < len(filtered.candidates); c++ {
|
||||
each := filtered.candidates[c]
|
||||
if each.route.pathExpr.Matcher.MatchString(pathRemainder) {
|
||||
matchingRoutes = append(matchingRoutes, each.route)
|
||||
}
|
||||
}
|
||||
return matchingRoutes
|
||||
}
|
||||
|
||||
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 1)
|
||||
func (r RouterJSR311) detectDispatcher(requestPath string, dispatchers []*WebService) (*WebService, string, error) {
|
||||
filtered := &sortableDispatcherCandidates{}
|
||||
for _, each := range dispatchers {
|
||||
matches := each.pathExpr.Matcher.FindStringSubmatch(requestPath)
|
||||
if matches != nil {
|
||||
filtered.candidates = append(filtered.candidates,
|
||||
dispatcherCandidate{each, matches[len(matches)-1], len(matches), each.pathExpr.LiteralCount, each.pathExpr.VarCount})
|
||||
}
|
||||
}
|
||||
if len(filtered.candidates) == 0 {
|
||||
if trace {
|
||||
traceLogger.Printf("no WebService was found to match URL path:%s\n", requestPath)
|
||||
}
|
||||
return nil, "", errors.New("not found")
|
||||
}
|
||||
sort.Sort(sort.Reverse(filtered))
|
||||
return filtered.candidates[0].dispatcher, filtered.candidates[0].finalMatch, nil
|
||||
}
|
||||
|
||||
// Types and functions to support the sorting of Routes
|
||||
|
||||
type routeCandidate struct {
|
||||
route Route
|
||||
matchesCount int // the number of capturing groups
|
||||
literalCount int // the number of literal characters (means those not resulting from template variable substitution)
|
||||
nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^ /]+?)’)
|
||||
}
|
||||
|
||||
func (r routeCandidate) expressionToMatch() string {
|
||||
return r.route.pathExpr.Source
|
||||
}
|
||||
|
||||
func (r routeCandidate) String() string {
|
||||
return fmt.Sprintf("(m=%d,l=%d,n=%d)", r.matchesCount, r.literalCount, r.nonDefaultCount)
|
||||
}
|
||||
|
||||
type sortableRouteCandidates struct {
|
||||
candidates []routeCandidate
|
||||
}
|
||||
|
||||
func (rcs *sortableRouteCandidates) Len() int {
|
||||
return len(rcs.candidates)
|
||||
}
|
||||
func (rcs *sortableRouteCandidates) Swap(i, j int) {
|
||||
rcs.candidates[i], rcs.candidates[j] = rcs.candidates[j], rcs.candidates[i]
|
||||
}
|
||||
func (rcs *sortableRouteCandidates) Less(i, j int) bool {
|
||||
ci := rcs.candidates[i]
|
||||
cj := rcs.candidates[j]
|
||||
// primary key
|
||||
if ci.literalCount < cj.literalCount {
|
||||
return true
|
||||
}
|
||||
if ci.literalCount > cj.literalCount {
|
||||
return false
|
||||
}
|
||||
// secundary key
|
||||
if ci.matchesCount < cj.matchesCount {
|
||||
return true
|
||||
}
|
||||
if ci.matchesCount > cj.matchesCount {
|
||||
return false
|
||||
}
|
||||
// tertiary key
|
||||
if ci.nonDefaultCount < cj.nonDefaultCount {
|
||||
return true
|
||||
}
|
||||
if ci.nonDefaultCount > cj.nonDefaultCount {
|
||||
return false
|
||||
}
|
||||
// quaternary key ("source" is interpreted as Path)
|
||||
return ci.route.Path < cj.route.Path
|
||||
}
|
||||
|
||||
// Types and functions to support the sorting of Dispatchers
|
||||
|
||||
type dispatcherCandidate struct {
|
||||
dispatcher *WebService
|
||||
finalMatch string
|
||||
matchesCount int // the number of capturing groups
|
||||
literalCount int // the number of literal characters (means those not resulting from template variable substitution)
|
||||
nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^ /]+?)’)
|
||||
}
|
||||
type sortableDispatcherCandidates struct {
|
||||
candidates []dispatcherCandidate
|
||||
}
|
||||
|
||||
func (dc *sortableDispatcherCandidates) Len() int {
|
||||
return len(dc.candidates)
|
||||
}
|
||||
func (dc *sortableDispatcherCandidates) Swap(i, j int) {
|
||||
dc.candidates[i], dc.candidates[j] = dc.candidates[j], dc.candidates[i]
|
||||
}
|
||||
func (dc *sortableDispatcherCandidates) Less(i, j int) bool {
|
||||
ci := dc.candidates[i]
|
||||
cj := dc.candidates[j]
|
||||
// primary key
|
||||
if ci.matchesCount < cj.matchesCount {
|
||||
return true
|
||||
}
|
||||
if ci.matchesCount > cj.matchesCount {
|
||||
return false
|
||||
}
|
||||
// secundary key
|
||||
if ci.literalCount < cj.literalCount {
|
||||
return true
|
||||
}
|
||||
if ci.literalCount > cj.literalCount {
|
||||
return false
|
||||
}
|
||||
// tertiary key
|
||||
return ci.nonDefaultCount < cj.nonDefaultCount
|
||||
}
|
||||
31
vendor/github.com/emicklei/go-restful/log/log.go
generated
vendored
Normal file
31
vendor/github.com/emicklei/go-restful/log/log.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
stdlog "log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Logger corresponds to a minimal subset of the interface satisfied by stdlib log.Logger
|
||||
type StdLogger interface {
|
||||
Print(v ...interface{})
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
var Logger StdLogger
|
||||
|
||||
func init() {
|
||||
// default Logger
|
||||
SetLogger(stdlog.New(os.Stderr, "[restful] ", stdlog.LstdFlags|stdlog.Lshortfile))
|
||||
}
|
||||
|
||||
func SetLogger(customLogger StdLogger) {
|
||||
Logger = customLogger
|
||||
}
|
||||
|
||||
func Print(v ...interface{}) {
|
||||
Logger.Print(v...)
|
||||
}
|
||||
|
||||
func Printf(format string, v ...interface{}) {
|
||||
Logger.Printf(format, v...)
|
||||
}
|
||||
32
vendor/github.com/emicklei/go-restful/logger.go
generated
vendored
Normal file
32
vendor/github.com/emicklei/go-restful/logger.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2014 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
import (
|
||||
"github.com/emicklei/go-restful/log"
|
||||
)
|
||||
|
||||
var trace bool = false
|
||||
var traceLogger log.StdLogger
|
||||
|
||||
func init() {
|
||||
traceLogger = log.Logger // use the package logger by default
|
||||
}
|
||||
|
||||
// TraceLogger enables detailed logging of Http request matching and filter invocation. Default no logger is set.
|
||||
// You may call EnableTracing() directly to enable trace logging to the package-wide logger.
|
||||
func TraceLogger(logger log.StdLogger) {
|
||||
traceLogger = logger
|
||||
EnableTracing(logger != nil)
|
||||
}
|
||||
|
||||
// expose the setter for the global logger on the top-level package
|
||||
func SetLogger(customLogger log.StdLogger) {
|
||||
log.SetLogger(customLogger)
|
||||
}
|
||||
|
||||
// EnableTracing can be used to Trace logging on and off.
|
||||
func EnableTracing(enabled bool) {
|
||||
trace = enabled
|
||||
}
|
||||
45
vendor/github.com/emicklei/go-restful/mime.go
generated
vendored
Normal file
45
vendor/github.com/emicklei/go-restful/mime.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package restful
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type mime struct {
|
||||
media string
|
||||
quality float64
|
||||
}
|
||||
|
||||
// insertMime adds a mime to a list and keeps it sorted by quality.
|
||||
func insertMime(l []mime, e mime) []mime {
|
||||
for i, each := range l {
|
||||
// if current mime has lower quality then insert before
|
||||
if e.quality > each.quality {
|
||||
left := append([]mime{}, l[0:i]...)
|
||||
return append(append(left, e), l[i:]...)
|
||||
}
|
||||
}
|
||||
return append(l, e)
|
||||
}
|
||||
|
||||
// sortedMimes returns a list of mime sorted (desc) by its specified quality.
|
||||
func sortedMimes(accept string) (sorted []mime) {
|
||||
for _, each := range strings.Split(accept, ",") {
|
||||
typeAndQuality := strings.Split(strings.Trim(each, " "), ";")
|
||||
if len(typeAndQuality) == 1 {
|
||||
sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0})
|
||||
} else {
|
||||
// take factor
|
||||
parts := strings.Split(typeAndQuality[1], "=")
|
||||
if len(parts) == 2 {
|
||||
f, err := strconv.ParseFloat(parts[1], 64)
|
||||
if err != nil {
|
||||
traceLogger.Printf("unable to parse quality in %s, %v", each, err)
|
||||
} else {
|
||||
sorted = insertMime(sorted, mime{typeAndQuality[0], f})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
26
vendor/github.com/emicklei/go-restful/options_filter.go
generated
vendored
Normal file
26
vendor/github.com/emicklei/go-restful/options_filter.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package restful
|
||||
|
||||
import "strings"
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
|
||||
// and provides the response with a set of allowed methods for the request URL Path.
|
||||
// As for any filter, you can also install it for a particular WebService within a Container.
|
||||
// Note: this filter is not needed when using CrossOriginResourceSharing (for CORS).
|
||||
func (c *Container) OPTIONSFilter(req *Request, resp *Response, chain *FilterChain) {
|
||||
if "OPTIONS" != req.Request.Method {
|
||||
chain.ProcessFilter(req, resp)
|
||||
return
|
||||
}
|
||||
resp.AddHeader(HEADER_Allow, strings.Join(c.computeAllowedMethods(req), ","))
|
||||
}
|
||||
|
||||
// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
|
||||
// and provides the response with a set of allowed methods for the request URL Path.
|
||||
// Note: this filter is not needed when using CrossOriginResourceSharing (for CORS).
|
||||
func OPTIONSFilter() FilterFunction {
|
||||
return DefaultContainer.OPTIONSFilter
|
||||
}
|
||||
114
vendor/github.com/emicklei/go-restful/parameter.go
generated
vendored
Normal file
114
vendor/github.com/emicklei/go-restful/parameter.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
const (
|
||||
// PathParameterKind = indicator of Request parameter type "path"
|
||||
PathParameterKind = iota
|
||||
|
||||
// QueryParameterKind = indicator of Request parameter type "query"
|
||||
QueryParameterKind
|
||||
|
||||
// BodyParameterKind = indicator of Request parameter type "body"
|
||||
BodyParameterKind
|
||||
|
||||
// HeaderParameterKind = indicator of Request parameter type "header"
|
||||
HeaderParameterKind
|
||||
|
||||
// FormParameterKind = indicator of Request parameter type "form"
|
||||
FormParameterKind
|
||||
)
|
||||
|
||||
// Parameter is for documententing the parameter used in a Http Request
|
||||
// ParameterData kinds are Path,Query and Body
|
||||
type Parameter struct {
|
||||
data *ParameterData
|
||||
}
|
||||
|
||||
// ParameterData represents the state of a Parameter.
|
||||
// It is made public to make it accessible to e.g. the Swagger package.
|
||||
type ParameterData struct {
|
||||
Name, Description, DataType, DataFormat string
|
||||
Kind int
|
||||
Required bool
|
||||
AllowableValues map[string]string
|
||||
AllowMultiple bool
|
||||
DefaultValue string
|
||||
}
|
||||
|
||||
// Data returns the state of the Parameter
|
||||
func (p *Parameter) Data() ParameterData {
|
||||
return *p.data
|
||||
}
|
||||
|
||||
// Kind returns the parameter type indicator (see const for valid values)
|
||||
func (p *Parameter) Kind() int {
|
||||
return p.data.Kind
|
||||
}
|
||||
|
||||
func (p *Parameter) bePath() *Parameter {
|
||||
p.data.Kind = PathParameterKind
|
||||
return p
|
||||
}
|
||||
func (p *Parameter) beQuery() *Parameter {
|
||||
p.data.Kind = QueryParameterKind
|
||||
return p
|
||||
}
|
||||
func (p *Parameter) beBody() *Parameter {
|
||||
p.data.Kind = BodyParameterKind
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Parameter) beHeader() *Parameter {
|
||||
p.data.Kind = HeaderParameterKind
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Parameter) beForm() *Parameter {
|
||||
p.data.Kind = FormParameterKind
|
||||
return p
|
||||
}
|
||||
|
||||
// Required sets the required field and returns the receiver
|
||||
func (p *Parameter) Required(required bool) *Parameter {
|
||||
p.data.Required = required
|
||||
return p
|
||||
}
|
||||
|
||||
// AllowMultiple sets the allowMultiple field and returns the receiver
|
||||
func (p *Parameter) AllowMultiple(multiple bool) *Parameter {
|
||||
p.data.AllowMultiple = multiple
|
||||
return p
|
||||
}
|
||||
|
||||
// AllowableValues sets the allowableValues field and returns the receiver
|
||||
func (p *Parameter) AllowableValues(values map[string]string) *Parameter {
|
||||
p.data.AllowableValues = values
|
||||
return p
|
||||
}
|
||||
|
||||
// DataType sets the dataType field and returns the receiver
|
||||
func (p *Parameter) DataType(typeName string) *Parameter {
|
||||
p.data.DataType = typeName
|
||||
return p
|
||||
}
|
||||
|
||||
// DataFormat sets the dataFormat field for Swagger UI
|
||||
func (p *Parameter) DataFormat(formatName string) *Parameter {
|
||||
p.data.DataFormat = formatName
|
||||
return p
|
||||
}
|
||||
|
||||
// DefaultValue sets the default value field and returns the receiver
|
||||
func (p *Parameter) DefaultValue(stringRepresentation string) *Parameter {
|
||||
p.data.DefaultValue = stringRepresentation
|
||||
return p
|
||||
}
|
||||
|
||||
// Description sets the description value field and returns the receiver
|
||||
func (p *Parameter) Description(doc string) *Parameter {
|
||||
p.data.Description = doc
|
||||
return p
|
||||
}
|
||||
69
vendor/github.com/emicklei/go-restful/path_expression.go
generated
vendored
Normal file
69
vendor/github.com/emicklei/go-restful/path_expression.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PathExpression holds a compiled path expression (RegExp) needed to match against
|
||||
// Http request paths and to extract path parameter values.
|
||||
type pathExpression struct {
|
||||
LiteralCount int // the number of literal characters (means those not resulting from template variable substitution)
|
||||
VarCount int // the number of named parameters (enclosed by {}) in the path
|
||||
Matcher *regexp.Regexp
|
||||
Source string // Path as defined by the RouteBuilder
|
||||
tokens []string
|
||||
}
|
||||
|
||||
// NewPathExpression creates a PathExpression from the input URL path.
|
||||
// Returns an error if the path is invalid.
|
||||
func newPathExpression(path string) (*pathExpression, error) {
|
||||
expression, literalCount, varCount, tokens := templateToRegularExpression(path)
|
||||
compiled, err := regexp.Compile(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pathExpression{literalCount, varCount, compiled, expression, tokens}, nil
|
||||
}
|
||||
|
||||
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3
|
||||
func templateToRegularExpression(template string) (expression string, literalCount int, varCount int, tokens []string) {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString("^")
|
||||
//tokens = strings.Split(template, "/")
|
||||
tokens = tokenizePath(template)
|
||||
for _, each := range tokens {
|
||||
if each == "" {
|
||||
continue
|
||||
}
|
||||
buffer.WriteString("/")
|
||||
if strings.HasPrefix(each, "{") {
|
||||
// check for regular expression in variable
|
||||
colon := strings.Index(each, ":")
|
||||
if colon != -1 {
|
||||
// extract expression
|
||||
paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1])
|
||||
if paramExpr == "*" { // special case
|
||||
buffer.WriteString("(.*)")
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("(%s)", paramExpr)) // between colon and closing moustache
|
||||
}
|
||||
} else {
|
||||
// plain var
|
||||
buffer.WriteString("([^/]+?)")
|
||||
}
|
||||
varCount += 1
|
||||
} else {
|
||||
literalCount += len(each)
|
||||
encoded := each // TODO URI encode
|
||||
buffer.WriteString(regexp.QuoteMeta(encoded))
|
||||
}
|
||||
}
|
||||
return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varCount, tokens
|
||||
}
|
||||
131
vendor/github.com/emicklei/go-restful/request.go
generated
vendored
Normal file
131
vendor/github.com/emicklei/go-restful/request.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var defaultRequestContentType string
|
||||
|
||||
var doCacheReadEntityBytes = true
|
||||
|
||||
// Request is a wrapper for a http Request that provides convenience methods
|
||||
type Request struct {
|
||||
Request *http.Request
|
||||
bodyContent *[]byte // to cache the request body for multiple reads of ReadEntity
|
||||
pathParameters map[string]string
|
||||
attributes map[string]interface{} // for storing request-scoped values
|
||||
selectedRoutePath string // root path + route path that matched the request, e.g. /meetings/{id}/attendees
|
||||
}
|
||||
|
||||
func NewRequest(httpRequest *http.Request) *Request {
|
||||
return &Request{
|
||||
Request: httpRequest,
|
||||
pathParameters: map[string]string{},
|
||||
attributes: map[string]interface{}{},
|
||||
} // empty parameters, attributes
|
||||
}
|
||||
|
||||
// If ContentType is missing or */* is given then fall back to this type, otherwise
|
||||
// a "Unable to unmarshal content of type:" response is returned.
|
||||
// Valid values are restful.MIME_JSON and restful.MIME_XML
|
||||
// Example:
|
||||
// restful.DefaultRequestContentType(restful.MIME_JSON)
|
||||
func DefaultRequestContentType(mime string) {
|
||||
defaultRequestContentType = mime
|
||||
}
|
||||
|
||||
// SetCacheReadEntity controls whether the response data ([]byte) is cached such that ReadEntity is repeatable.
|
||||
// Default is true (due to backwardcompatibility). For better performance, you should set it to false if you don't need it.
|
||||
func SetCacheReadEntity(doCache bool) {
|
||||
doCacheReadEntityBytes = doCache
|
||||
}
|
||||
|
||||
// PathParameter accesses the Path parameter value by its name
|
||||
func (r *Request) PathParameter(name string) string {
|
||||
return r.pathParameters[name]
|
||||
}
|
||||
|
||||
// PathParameters accesses the Path parameter values
|
||||
func (r *Request) PathParameters() map[string]string {
|
||||
return r.pathParameters
|
||||
}
|
||||
|
||||
// QueryParameter returns the (first) Query parameter value by its name
|
||||
func (r *Request) QueryParameter(name string) string {
|
||||
return r.Request.FormValue(name)
|
||||
}
|
||||
|
||||
// BodyParameter parses the body of the request (once for typically a POST or a PUT) and returns the value of the given name or an error.
|
||||
func (r *Request) BodyParameter(name string) (string, error) {
|
||||
err := r.Request.ParseForm()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return r.Request.PostFormValue(name), nil
|
||||
}
|
||||
|
||||
// HeaderParameter returns the HTTP Header value of a Header name or empty if missing
|
||||
func (r *Request) HeaderParameter(name string) string {
|
||||
return r.Request.Header.Get(name)
|
||||
}
|
||||
|
||||
// ReadEntity checks the Accept header and reads the content into the entityPointer.
|
||||
func (r *Request) ReadEntity(entityPointer interface{}) (err error) {
|
||||
contentType := r.Request.Header.Get(HEADER_ContentType)
|
||||
contentEncoding := r.Request.Header.Get(HEADER_ContentEncoding)
|
||||
|
||||
// OLD feature, cache the body for reads
|
||||
if doCacheReadEntityBytes {
|
||||
if r.bodyContent == nil {
|
||||
data, err := ioutil.ReadAll(r.Request.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.bodyContent = &data
|
||||
}
|
||||
r.Request.Body = ioutil.NopCloser(bytes.NewReader(*r.bodyContent))
|
||||
}
|
||||
|
||||
// check if the request body needs decompression
|
||||
if ENCODING_GZIP == contentEncoding {
|
||||
gzipReader := currentCompressorProvider.AcquireGzipReader()
|
||||
defer currentCompressorProvider.ReleaseGzipReader(gzipReader)
|
||||
gzipReader.Reset(r.Request.Body)
|
||||
r.Request.Body = gzipReader
|
||||
} else if ENCODING_DEFLATE == contentEncoding {
|
||||
zlibReader, err := zlib.NewReader(r.Request.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Request.Body = zlibReader
|
||||
}
|
||||
|
||||
// lookup the EntityReader
|
||||
entityReader, ok := entityAccessRegistry.accessorAt(contentType)
|
||||
if !ok {
|
||||
return NewError(http.StatusBadRequest, "Unable to unmarshal content of type:"+contentType)
|
||||
}
|
||||
return entityReader.Read(r, entityPointer)
|
||||
}
|
||||
|
||||
// SetAttribute adds or replaces the attribute with the given value.
|
||||
func (r *Request) SetAttribute(name string, value interface{}) {
|
||||
r.attributes[name] = value
|
||||
}
|
||||
|
||||
// Attribute returns the value associated to the given name. Returns nil if absent.
|
||||
func (r Request) Attribute(name string) interface{} {
|
||||
return r.attributes[name]
|
||||
}
|
||||
|
||||
// SelectedRoutePath root path + route path that matched the request, e.g. /meetings/{id}/attendees
|
||||
func (r Request) SelectedRoutePath() string {
|
||||
return r.selectedRoutePath
|
||||
}
|
||||
235
vendor/github.com/emicklei/go-restful/response.go
generated
vendored
Normal file
235
vendor/github.com/emicklei/go-restful/response.go
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// DEPRECATED, use DefaultResponseContentType(mime)
|
||||
var DefaultResponseMimeType string
|
||||
|
||||
//PrettyPrintResponses controls the indentation feature of XML and JSON serialization
|
||||
var PrettyPrintResponses = true
|
||||
|
||||
// Response is a wrapper on the actual http ResponseWriter
|
||||
// It provides several convenience methods to prepare and write response content.
|
||||
type Response struct {
|
||||
http.ResponseWriter
|
||||
requestAccept string // mime-type what the Http Request says it wants to receive
|
||||
routeProduces []string // mime-types what the Route says it can produce
|
||||
statusCode int // HTTP status code that has been written explicity (if zero then net/http has written 200)
|
||||
contentLength int // number of bytes written for the response body
|
||||
prettyPrint bool // controls the indentation feature of XML and JSON serialization. It is initialized using var PrettyPrintResponses.
|
||||
err error // err property is kept when WriteError is called
|
||||
}
|
||||
|
||||
// Creates a new response based on a http ResponseWriter.
|
||||
func NewResponse(httpWriter http.ResponseWriter) *Response {
|
||||
return &Response{httpWriter, "", []string{}, http.StatusOK, 0, PrettyPrintResponses, nil} // empty content-types
|
||||
}
|
||||
|
||||
// If Accept header matching fails, fall back to this type.
|
||||
// Valid values are restful.MIME_JSON and restful.MIME_XML
|
||||
// Example:
|
||||
// restful.DefaultResponseContentType(restful.MIME_JSON)
|
||||
func DefaultResponseContentType(mime string) {
|
||||
DefaultResponseMimeType = mime
|
||||
}
|
||||
|
||||
// InternalServerError writes the StatusInternalServerError header.
|
||||
// DEPRECATED, use WriteErrorString(http.StatusInternalServerError,reason)
|
||||
func (r Response) InternalServerError() Response {
|
||||
r.WriteHeader(http.StatusInternalServerError)
|
||||
return r
|
||||
}
|
||||
|
||||
// PrettyPrint changes whether this response must produce pretty (line-by-line, indented) JSON or XML output.
|
||||
func (r *Response) PrettyPrint(bePretty bool) {
|
||||
r.prettyPrint = bePretty
|
||||
}
|
||||
|
||||
// AddHeader is a shortcut for .Header().Add(header,value)
|
||||
func (r Response) AddHeader(header string, value string) Response {
|
||||
r.Header().Add(header, value)
|
||||
return r
|
||||
}
|
||||
|
||||
// SetRequestAccepts tells the response what Mime-type(s) the HTTP request said it wants to accept. Exposed for testing.
|
||||
func (r *Response) SetRequestAccepts(mime string) {
|
||||
r.requestAccept = mime
|
||||
}
|
||||
|
||||
// EntityWriter returns the registered EntityWriter that the entity (requested resource)
|
||||
// can write according to what the request wants (Accept) and what the Route can produce or what the restful defaults say.
|
||||
// If called before WriteEntity and WriteHeader then a false return value can be used to write a 406: Not Acceptable.
|
||||
func (r *Response) EntityWriter() (EntityReaderWriter, bool) {
|
||||
sorted := sortedMimes(r.requestAccept)
|
||||
for _, eachAccept := range sorted {
|
||||
for _, eachProduce := range r.routeProduces {
|
||||
if eachProduce == eachAccept.media {
|
||||
if w, ok := entityAccessRegistry.accessorAt(eachAccept.media); ok {
|
||||
return w, true
|
||||
}
|
||||
}
|
||||
}
|
||||
if eachAccept.media == "*/*" {
|
||||
for _, each := range r.routeProduces {
|
||||
if w, ok := entityAccessRegistry.accessorAt(each); ok {
|
||||
return w, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if requestAccept is empty
|
||||
writer, ok := entityAccessRegistry.accessorAt(r.requestAccept)
|
||||
if !ok {
|
||||
// if not registered then fallback to the defaults (if set)
|
||||
if DefaultResponseMimeType == MIME_JSON {
|
||||
return entityAccessRegistry.accessorAt(MIME_JSON)
|
||||
}
|
||||
if DefaultResponseMimeType == MIME_XML {
|
||||
return entityAccessRegistry.accessorAt(MIME_XML)
|
||||
}
|
||||
// Fallback to whatever the route says it can produce.
|
||||
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||
for _, each := range r.routeProduces {
|
||||
if w, ok := entityAccessRegistry.accessorAt(each); ok {
|
||||
return w, true
|
||||
}
|
||||
}
|
||||
if trace {
|
||||
traceLogger.Printf("no registered EntityReaderWriter found for %s", r.requestAccept)
|
||||
}
|
||||
}
|
||||
return writer, ok
|
||||
}
|
||||
|
||||
// WriteEntity calls WriteHeaderAndEntity with Http Status OK (200)
|
||||
func (r *Response) WriteEntity(value interface{}) error {
|
||||
return r.WriteHeaderAndEntity(http.StatusOK, value)
|
||||
}
|
||||
|
||||
// WriteHeaderAndEntity marshals the value using the representation denoted by the Accept Header and the registered EntityWriters.
|
||||
// If no Accept header is specified (or */*) then respond with the Content-Type as specified by the first in the Route.Produces.
|
||||
// If an Accept header is specified then respond with the Content-Type as specified by the first in the Route.Produces that is matched with the Accept header.
|
||||
// If the value is nil then no response is send except for the Http status. You may want to call WriteHeader(http.StatusNotFound) instead.
|
||||
// If there is no writer available that can represent the value in the requested MIME type then Http Status NotAcceptable is written.
|
||||
// Current implementation ignores any q-parameters in the Accept Header.
|
||||
// Returns an error if the value could not be written on the response.
|
||||
func (r *Response) WriteHeaderAndEntity(status int, value interface{}) error {
|
||||
writer, ok := r.EntityWriter()
|
||||
if !ok {
|
||||
r.WriteHeader(http.StatusNotAcceptable)
|
||||
return nil
|
||||
}
|
||||
return writer.Write(r, status, value)
|
||||
}
|
||||
|
||||
// WriteAsXml is a convenience method for writing a value in xml (requires Xml tags on the value)
|
||||
// It uses the standard encoding/xml package for marshalling the valuel ; not using a registered EntityReaderWriter.
|
||||
func (r *Response) WriteAsXml(value interface{}) error {
|
||||
return writeXML(r, http.StatusOK, MIME_XML, value)
|
||||
}
|
||||
|
||||
// WriteHeaderAndXml is a convenience method for writing a status and value in xml (requires Xml tags on the value)
|
||||
// It uses the standard encoding/xml package for marshalling the valuel ; not using a registered EntityReaderWriter.
|
||||
func (r *Response) WriteHeaderAndXml(status int, value interface{}) error {
|
||||
return writeXML(r, status, MIME_XML, value)
|
||||
}
|
||||
|
||||
// WriteAsJson is a convenience method for writing a value in json.
|
||||
// It uses the standard encoding/json package for marshalling the valuel ; not using a registered EntityReaderWriter.
|
||||
func (r *Response) WriteAsJson(value interface{}) error {
|
||||
return writeJSON(r, http.StatusOK, MIME_JSON, value)
|
||||
}
|
||||
|
||||
// WriteJson is a convenience method for writing a value in Json with a given Content-Type.
|
||||
// It uses the standard encoding/json package for marshalling the valuel ; not using a registered EntityReaderWriter.
|
||||
func (r *Response) WriteJson(value interface{}, contentType string) error {
|
||||
return writeJSON(r, http.StatusOK, contentType, value)
|
||||
}
|
||||
|
||||
// WriteHeaderAndJson is a convenience method for writing the status and a value in Json with a given Content-Type.
|
||||
// It uses the standard encoding/json package for marshalling the value ; not using a registered EntityReaderWriter.
|
||||
func (r *Response) WriteHeaderAndJson(status int, value interface{}, contentType string) error {
|
||||
return writeJSON(r, status, contentType, value)
|
||||
}
|
||||
|
||||
// WriteError write the http status and the error string on the response.
|
||||
func (r *Response) WriteError(httpStatus int, err error) error {
|
||||
r.err = err
|
||||
return r.WriteErrorString(httpStatus, err.Error())
|
||||
}
|
||||
|
||||
// WriteServiceError is a convenience method for a responding with a status and a ServiceError
|
||||
func (r *Response) WriteServiceError(httpStatus int, err ServiceError) error {
|
||||
r.err = err
|
||||
return r.WriteHeaderAndEntity(httpStatus, err)
|
||||
}
|
||||
|
||||
// WriteErrorString is a convenience method for an error status with the actual error
|
||||
func (r *Response) WriteErrorString(httpStatus int, errorReason string) error {
|
||||
if r.err == nil {
|
||||
// if not called from WriteError
|
||||
r.err = errors.New(errorReason)
|
||||
}
|
||||
r.WriteHeader(httpStatus)
|
||||
if _, err := r.Write([]byte(errorReason)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush implements http.Flusher interface, which sends any buffered data to the client.
|
||||
func (r *Response) Flush() {
|
||||
if f, ok := r.ResponseWriter.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
} else if trace {
|
||||
traceLogger.Printf("ResponseWriter %v doesn't support Flush", r)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteHeader is overridden to remember the Status Code that has been written.
|
||||
// Changes to the Header of the response have no effect after this.
|
||||
func (r *Response) WriteHeader(httpStatus int) {
|
||||
r.statusCode = httpStatus
|
||||
r.ResponseWriter.WriteHeader(httpStatus)
|
||||
}
|
||||
|
||||
// StatusCode returns the code that has been written using WriteHeader.
|
||||
func (r Response) StatusCode() int {
|
||||
if 0 == r.statusCode {
|
||||
// no status code has been written yet; assume OK
|
||||
return http.StatusOK
|
||||
}
|
||||
return r.statusCode
|
||||
}
|
||||
|
||||
// Write writes the data to the connection as part of an HTTP reply.
|
||||
// Write is part of http.ResponseWriter interface.
|
||||
func (r *Response) Write(bytes []byte) (int, error) {
|
||||
written, err := r.ResponseWriter.Write(bytes)
|
||||
r.contentLength += written
|
||||
return written, err
|
||||
}
|
||||
|
||||
// ContentLength returns the number of bytes written for the response content.
|
||||
// Note that this value is only correct if all data is written through the Response using its Write* methods.
|
||||
// Data written directly using the underlying http.ResponseWriter is not accounted for.
|
||||
func (r Response) ContentLength() int {
|
||||
return r.contentLength
|
||||
}
|
||||
|
||||
// CloseNotify is part of http.CloseNotifier interface
|
||||
func (r Response) CloseNotify() <-chan bool {
|
||||
return r.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// Error returns the err created by WriteError
|
||||
func (r Response) Error() error {
|
||||
return r.err
|
||||
}
|
||||
183
vendor/github.com/emicklei/go-restful/route.go
generated
vendored
Normal file
183
vendor/github.com/emicklei/go-restful/route.go
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RouteFunction declares the signature of a function that can be bound to a Route.
|
||||
type RouteFunction func(*Request, *Response)
|
||||
|
||||
// Route binds a HTTP Method,Path,Consumes combination to a RouteFunction.
|
||||
type Route struct {
|
||||
Method string
|
||||
Produces []string
|
||||
Consumes []string
|
||||
Path string // webservice root path + described path
|
||||
Function RouteFunction
|
||||
Filters []FilterFunction
|
||||
|
||||
// cached values for dispatching
|
||||
relativePath string
|
||||
pathParts []string
|
||||
pathExpr *pathExpression // cached compilation of relativePath as RegExp
|
||||
|
||||
// documentation
|
||||
Doc string
|
||||
Notes string
|
||||
Operation string
|
||||
ParameterDocs []*Parameter
|
||||
ResponseErrors map[int]ResponseError
|
||||
ReadSample, WriteSample interface{} // structs that model an example request or response payload
|
||||
}
|
||||
|
||||
// Initialize for Route
|
||||
func (r *Route) postBuild() {
|
||||
r.pathParts = tokenizePath(r.Path)
|
||||
}
|
||||
|
||||
// Create Request and Response from their http versions
|
||||
func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
|
||||
params := r.extractParameters(httpRequest.URL.Path)
|
||||
wrappedRequest := NewRequest(httpRequest)
|
||||
wrappedRequest.pathParameters = params
|
||||
wrappedRequest.selectedRoutePath = r.Path
|
||||
wrappedResponse := NewResponse(httpWriter)
|
||||
wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept)
|
||||
wrappedResponse.routeProduces = r.Produces
|
||||
return wrappedRequest, wrappedResponse
|
||||
}
|
||||
|
||||
// dispatchWithFilters call the function after passing through its own filters
|
||||
func (r *Route) dispatchWithFilters(wrappedRequest *Request, wrappedResponse *Response) {
|
||||
if len(r.Filters) > 0 {
|
||||
chain := FilterChain{Filters: r.Filters, Target: r.Function}
|
||||
chain.ProcessFilter(wrappedRequest, wrappedResponse)
|
||||
} else {
|
||||
// unfiltered
|
||||
r.Function(wrappedRequest, wrappedResponse)
|
||||
}
|
||||
}
|
||||
|
||||
// Return whether the mimeType matches to what this Route can produce.
|
||||
func (r Route) matchesAccept(mimeTypesWithQuality string) bool {
|
||||
parts := strings.Split(mimeTypesWithQuality, ",")
|
||||
for _, each := range parts {
|
||||
var withoutQuality string
|
||||
if strings.Contains(each, ";") {
|
||||
withoutQuality = strings.Split(each, ";")[0]
|
||||
} else {
|
||||
withoutQuality = each
|
||||
}
|
||||
// trim before compare
|
||||
withoutQuality = strings.Trim(withoutQuality, " ")
|
||||
if withoutQuality == "*/*" {
|
||||
return true
|
||||
}
|
||||
for _, producibleType := range r.Produces {
|
||||
if producibleType == "*/*" || producibleType == withoutQuality {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Return whether this Route can consume content with a type specified by mimeTypes (can be empty).
|
||||
func (r Route) matchesContentType(mimeTypes string) bool {
|
||||
|
||||
if len(r.Consumes) == 0 {
|
||||
// did not specify what it can consume ; any media type (“*/*”) is assumed
|
||||
return true
|
||||
}
|
||||
|
||||
if len(mimeTypes) == 0 {
|
||||
// idempotent methods with (most-likely or garanteed) empty content match missing Content-Type
|
||||
m := r.Method
|
||||
if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" {
|
||||
return true
|
||||
}
|
||||
// proceed with default
|
||||
mimeTypes = MIME_OCTET
|
||||
}
|
||||
|
||||
parts := strings.Split(mimeTypes, ",")
|
||||
for _, each := range parts {
|
||||
var contentType string
|
||||
if strings.Contains(each, ";") {
|
||||
contentType = strings.Split(each, ";")[0]
|
||||
} else {
|
||||
contentType = each
|
||||
}
|
||||
// trim before compare
|
||||
contentType = strings.Trim(contentType, " ")
|
||||
for _, consumeableType := range r.Consumes {
|
||||
if consumeableType == "*/*" || consumeableType == contentType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Extract the parameters from the request url path
|
||||
func (r Route) extractParameters(urlPath string) map[string]string {
|
||||
urlParts := tokenizePath(urlPath)
|
||||
pathParameters := map[string]string{}
|
||||
for i, key := range r.pathParts {
|
||||
var value string
|
||||
if i >= len(urlParts) {
|
||||
value = ""
|
||||
} else {
|
||||
value = urlParts[i]
|
||||
}
|
||||
if strings.HasPrefix(key, "{") { // path-parameter
|
||||
if colon := strings.Index(key, ":"); colon != -1 {
|
||||
// extract by regex
|
||||
regPart := key[colon+1 : len(key)-1]
|
||||
keyPart := key[1:colon]
|
||||
if regPart == "*" {
|
||||
pathParameters[keyPart] = untokenizePath(i, urlParts)
|
||||
break
|
||||
} else {
|
||||
pathParameters[keyPart] = value
|
||||
}
|
||||
} else {
|
||||
// without enclosing {}
|
||||
pathParameters[key[1:len(key)-1]] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
return pathParameters
|
||||
}
|
||||
|
||||
// Untokenize back into an URL path using the slash separator
|
||||
func untokenizePath(offset int, parts []string) string {
|
||||
var buffer bytes.Buffer
|
||||
for p := offset; p < len(parts); p++ {
|
||||
buffer.WriteString(parts[p])
|
||||
// do not end
|
||||
if p < len(parts)-1 {
|
||||
buffer.WriteString("/")
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Tokenize an URL path using the slash separator ; the result does not have empty tokens
|
||||
func tokenizePath(path string) []string {
|
||||
if "/" == path {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(strings.Trim(path, "/"), "/")
|
||||
}
|
||||
|
||||
// for debugging
|
||||
func (r Route) String() string {
|
||||
return r.Method + " " + r.Path
|
||||
}
|
||||
240
vendor/github.com/emicklei/go-restful/route_builder.go
generated
vendored
Normal file
240
vendor/github.com/emicklei/go-restful/route_builder.go
generated
vendored
Normal file
@@ -0,0 +1,240 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful/log"
|
||||
)
|
||||
|
||||
// RouteBuilder is a helper to construct Routes.
|
||||
type RouteBuilder struct {
|
||||
rootPath string
|
||||
currentPath string
|
||||
produces []string
|
||||
consumes []string
|
||||
httpMethod string // required
|
||||
function RouteFunction // required
|
||||
filters []FilterFunction
|
||||
// documentation
|
||||
doc string
|
||||
notes string
|
||||
operation string
|
||||
readSample, writeSample interface{}
|
||||
parameters []*Parameter
|
||||
errorMap map[int]ResponseError
|
||||
}
|
||||
|
||||
// Do evaluates each argument with the RouteBuilder itself.
|
||||
// This allows you to follow DRY principles without breaking the fluent programming style.
|
||||
// Example:
|
||||
// ws.Route(ws.DELETE("/{name}").To(t.deletePerson).Do(Returns200, Returns500))
|
||||
//
|
||||
// func Returns500(b *RouteBuilder) {
|
||||
// b.Returns(500, "Internal Server Error", restful.ServiceError{})
|
||||
// }
|
||||
func (b *RouteBuilder) Do(oneArgBlocks ...func(*RouteBuilder)) *RouteBuilder {
|
||||
for _, each := range oneArgBlocks {
|
||||
each(b)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// To bind the route to a function.
|
||||
// If this route is matched with the incoming Http Request then call this function with the *Request,*Response pair. Required.
|
||||
func (b *RouteBuilder) To(function RouteFunction) *RouteBuilder {
|
||||
b.function = function
|
||||
return b
|
||||
}
|
||||
|
||||
// Method specifies what HTTP method to match. Required.
|
||||
func (b *RouteBuilder) Method(method string) *RouteBuilder {
|
||||
b.httpMethod = method
|
||||
return b
|
||||
}
|
||||
|
||||
// Produces specifies what MIME types can be produced ; the matched one will appear in the Content-Type Http header.
|
||||
func (b *RouteBuilder) Produces(mimeTypes ...string) *RouteBuilder {
|
||||
b.produces = mimeTypes
|
||||
return b
|
||||
}
|
||||
|
||||
// Consumes specifies what MIME types can be consumes ; the Accept Http header must matched any of these
|
||||
func (b *RouteBuilder) Consumes(mimeTypes ...string) *RouteBuilder {
|
||||
b.consumes = mimeTypes
|
||||
return b
|
||||
}
|
||||
|
||||
// Path specifies the relative (w.r.t WebService root path) URL path to match. Default is "/".
|
||||
func (b *RouteBuilder) Path(subPath string) *RouteBuilder {
|
||||
b.currentPath = subPath
|
||||
return b
|
||||
}
|
||||
|
||||
// Doc tells what this route is all about. Optional.
|
||||
func (b *RouteBuilder) Doc(documentation string) *RouteBuilder {
|
||||
b.doc = documentation
|
||||
return b
|
||||
}
|
||||
|
||||
// A verbose explanation of the operation behavior. Optional.
|
||||
func (b *RouteBuilder) Notes(notes string) *RouteBuilder {
|
||||
b.notes = notes
|
||||
return b
|
||||
}
|
||||
|
||||
// Reads tells what resource type will be read from the request payload. Optional.
|
||||
// A parameter of type "body" is added ,required is set to true and the dataType is set to the qualified name of the sample's type.
|
||||
func (b *RouteBuilder) Reads(sample interface{}) *RouteBuilder {
|
||||
b.readSample = sample
|
||||
typeAsName := reflect.TypeOf(sample).String()
|
||||
bodyParameter := &Parameter{&ParameterData{Name: "body"}}
|
||||
bodyParameter.beBody()
|
||||
bodyParameter.Required(true)
|
||||
bodyParameter.DataType(typeAsName)
|
||||
b.Param(bodyParameter)
|
||||
return b
|
||||
}
|
||||
|
||||
// ParameterNamed returns a Parameter already known to the RouteBuilder. Returns nil if not.
|
||||
// Use this to modify or extend information for the Parameter (through its Data()).
|
||||
func (b RouteBuilder) ParameterNamed(name string) (p *Parameter) {
|
||||
for _, each := range b.parameters {
|
||||
if each.Data().Name == name {
|
||||
return each
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Writes tells what resource type will be written as the response payload. Optional.
|
||||
func (b *RouteBuilder) Writes(sample interface{}) *RouteBuilder {
|
||||
b.writeSample = sample
|
||||
return b
|
||||
}
|
||||
|
||||
// Param allows you to document the parameters of the Route. It adds a new Parameter (does not check for duplicates).
|
||||
func (b *RouteBuilder) Param(parameter *Parameter) *RouteBuilder {
|
||||
if b.parameters == nil {
|
||||
b.parameters = []*Parameter{}
|
||||
}
|
||||
b.parameters = append(b.parameters, parameter)
|
||||
return b
|
||||
}
|
||||
|
||||
// Operation allows you to document what the acutal method/function call is of the Route.
|
||||
// Unless called, the operation name is derived from the RouteFunction set using To(..).
|
||||
func (b *RouteBuilder) Operation(name string) *RouteBuilder {
|
||||
b.operation = name
|
||||
return b
|
||||
}
|
||||
|
||||
// ReturnsError is deprecated, use Returns instead.
|
||||
func (b *RouteBuilder) ReturnsError(code int, message string, model interface{}) *RouteBuilder {
|
||||
log.Print("ReturnsError is deprecated, use Returns instead.")
|
||||
return b.Returns(code, message, model)
|
||||
}
|
||||
|
||||
// Returns allows you to document what responses (errors or regular) can be expected.
|
||||
// The model parameter is optional ; either pass a struct instance or use nil if not applicable.
|
||||
func (b *RouteBuilder) Returns(code int, message string, model interface{}) *RouteBuilder {
|
||||
err := ResponseError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Model: model,
|
||||
}
|
||||
// lazy init because there is no NewRouteBuilder (yet)
|
||||
if b.errorMap == nil {
|
||||
b.errorMap = map[int]ResponseError{}
|
||||
}
|
||||
b.errorMap[code] = err
|
||||
return b
|
||||
}
|
||||
|
||||
type ResponseError struct {
|
||||
Code int
|
||||
Message string
|
||||
Model interface{}
|
||||
}
|
||||
|
||||
func (b *RouteBuilder) servicePath(path string) *RouteBuilder {
|
||||
b.rootPath = path
|
||||
return b
|
||||
}
|
||||
|
||||
// Filter appends a FilterFunction to the end of filters for this Route to build.
|
||||
func (b *RouteBuilder) Filter(filter FilterFunction) *RouteBuilder {
|
||||
b.filters = append(b.filters, filter)
|
||||
return b
|
||||
}
|
||||
|
||||
// If no specific Route path then set to rootPath
|
||||
// If no specific Produces then set to rootProduces
|
||||
// If no specific Consumes then set to rootConsumes
|
||||
func (b *RouteBuilder) copyDefaults(rootProduces, rootConsumes []string) {
|
||||
if len(b.produces) == 0 {
|
||||
b.produces = rootProduces
|
||||
}
|
||||
if len(b.consumes) == 0 {
|
||||
b.consumes = rootConsumes
|
||||
}
|
||||
}
|
||||
|
||||
// Build creates a new Route using the specification details collected by the RouteBuilder
|
||||
func (b *RouteBuilder) Build() Route {
|
||||
pathExpr, err := newPathExpression(b.currentPath)
|
||||
if err != nil {
|
||||
log.Printf("[restful] Invalid path:%s because:%v", b.currentPath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if b.function == nil {
|
||||
log.Printf("[restful] No function specified for route:" + b.currentPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
operationName := b.operation
|
||||
if len(operationName) == 0 && b.function != nil {
|
||||
// extract from definition
|
||||
operationName = nameOfFunction(b.function)
|
||||
}
|
||||
route := Route{
|
||||
Method: b.httpMethod,
|
||||
Path: concatPath(b.rootPath, b.currentPath),
|
||||
Produces: b.produces,
|
||||
Consumes: b.consumes,
|
||||
Function: b.function,
|
||||
Filters: b.filters,
|
||||
relativePath: b.currentPath,
|
||||
pathExpr: pathExpr,
|
||||
Doc: b.doc,
|
||||
Notes: b.notes,
|
||||
Operation: operationName,
|
||||
ParameterDocs: b.parameters,
|
||||
ResponseErrors: b.errorMap,
|
||||
ReadSample: b.readSample,
|
||||
WriteSample: b.writeSample}
|
||||
route.postBuild()
|
||||
return route
|
||||
}
|
||||
|
||||
func concatPath(path1, path2 string) string {
|
||||
return strings.TrimRight(path1, "/") + "/" + strings.TrimLeft(path2, "/")
|
||||
}
|
||||
|
||||
// nameOfFunction returns the short name of the function f for documentation.
|
||||
// It uses a runtime feature for debugging ; its value may change for later Go versions.
|
||||
func nameOfFunction(f interface{}) string {
|
||||
fun := runtime.FuncForPC(reflect.ValueOf(f).Pointer())
|
||||
tokenized := strings.Split(fun.Name(), ".")
|
||||
last := tokenized[len(tokenized)-1]
|
||||
last = strings.TrimSuffix(last, ")·fm") // < Go 1.5
|
||||
last = strings.TrimSuffix(last, ")-fm") // Go 1.5
|
||||
last = strings.TrimSuffix(last, "·fm") // < Go 1.5
|
||||
last = strings.TrimSuffix(last, "-fm") // Go 1.5
|
||||
return last
|
||||
}
|
||||
18
vendor/github.com/emicklei/go-restful/router.go
generated
vendored
Normal file
18
vendor/github.com/emicklei/go-restful/router.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import "net/http"
|
||||
|
||||
// A RouteSelector finds the best matching Route given the input HTTP Request
|
||||
type RouteSelector interface {
|
||||
|
||||
// SelectRoute finds a Route given the input HTTP Request and a list of WebServices.
|
||||
// It returns a selected Route and its containing WebService or an error indicating
|
||||
// a problem.
|
||||
SelectRoute(
|
||||
webServices []*WebService,
|
||||
httpRequest *http.Request) (selectedService *WebService, selected *Route, err error)
|
||||
}
|
||||
23
vendor/github.com/emicklei/go-restful/service_error.go
generated
vendored
Normal file
23
vendor/github.com/emicklei/go-restful/service_error.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package restful
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ServiceError is a transport object to pass information about a non-Http error occurred in a WebService while processing a request.
|
||||
type ServiceError struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
// NewError returns a ServiceError using the code and reason
|
||||
func NewError(code int, message string) ServiceError {
|
||||
return ServiceError{Code: code, Message: message}
|
||||
}
|
||||
|
||||
// Error returns a text representation of the service error
|
||||
func (s ServiceError) Error() string {
|
||||
return fmt.Sprintf("[ServiceError:%v] %v", s.Code, s.Message)
|
||||
}
|
||||
43
vendor/github.com/emicklei/go-restful/swagger/CHANGES.md
generated
vendored
Normal file
43
vendor/github.com/emicklei/go-restful/swagger/CHANGES.md
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
Change history of swagger
|
||||
=
|
||||
2015-10-16
|
||||
- add type override mechanism for swagger models (MR 254, nathanejohnson)
|
||||
- replace uses of wildcard in generated apidocs (issue 251)
|
||||
|
||||
2015-05-25
|
||||
- (api break) changed the type of Properties in Model
|
||||
- (api break) changed the type of Models in ApiDeclaration
|
||||
- (api break) changed the parameter type of PostBuildDeclarationMapFunc
|
||||
|
||||
2015-04-09
|
||||
- add ModelBuildable interface for customization of Model
|
||||
|
||||
2015-03-17
|
||||
- preserve order of Routes per WebService in Swagger listing
|
||||
- fix use of $ref and type in Swagger models
|
||||
- add api version to listing
|
||||
|
||||
2014-11-14
|
||||
- operation parameters are now sorted using ordering path,query,form,header,body
|
||||
|
||||
2014-11-12
|
||||
- respect omitempty tag value for embedded structs
|
||||
- expose ApiVersion of WebService to Swagger ApiDeclaration
|
||||
|
||||
2014-05-29
|
||||
- (api add) Ability to define custom http.Handler to serve swagger-ui static files
|
||||
|
||||
2014-05-04
|
||||
- (fix) include model for array element type of response
|
||||
|
||||
2014-01-03
|
||||
- (fix) do not add primitive type to the Api models
|
||||
|
||||
2013-11-27
|
||||
- (fix) make Swagger work for WebServices with root ("/" or "") paths
|
||||
|
||||
2013-10-29
|
||||
- (api add) package variable LogInfo to customize logging function
|
||||
|
||||
2013-10-15
|
||||
- upgraded to spec version 1.2 (https://github.com/wordnik/swagger-core/wiki/1.2-transition)
|
||||
76
vendor/github.com/emicklei/go-restful/swagger/README.md
generated
vendored
Normal file
76
vendor/github.com/emicklei/go-restful/swagger/README.md
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
How to use Swagger UI with go-restful
|
||||
=
|
||||
|
||||
Get the Swagger UI sources (version 1.2 only)
|
||||
|
||||
git clone https://github.com/wordnik/swagger-ui.git
|
||||
|
||||
The project contains a "dist" folder.
|
||||
Its contents has all the Swagger UI files you need.
|
||||
|
||||
The `index.html` has an `url` set to `http://petstore.swagger.wordnik.com/api/api-docs`.
|
||||
You need to change that to match your WebService JSON endpoint e.g. `http://localhost:8080/apidocs.json`
|
||||
|
||||
Now, you can install the Swagger WebService for serving the Swagger specification in JSON.
|
||||
|
||||
config := swagger.Config{
|
||||
WebServices: restful.RegisteredWebServices(),
|
||||
ApiPath: "/apidocs.json",
|
||||
SwaggerPath: "/apidocs/",
|
||||
SwaggerFilePath: "/Users/emicklei/Projects/swagger-ui/dist"}
|
||||
swagger.InstallSwaggerService(config)
|
||||
|
||||
|
||||
Documenting Structs
|
||||
--
|
||||
|
||||
Currently there are 2 ways to document your structs in the go-restful Swagger.
|
||||
|
||||
###### By using struct tags
|
||||
- Use tag "description" to annotate a struct field with a description to show in the UI
|
||||
- Use tag "modelDescription" to annotate the struct itself with a description to show in the UI. The tag can be added in an field of the struct and in case that there are multiple definition, they will be appended with an empty line.
|
||||
|
||||
###### By using the SwaggerDoc method
|
||||
Here is an example with an `Address` struct and the documentation for each of the fields. The `""` is a special entry for **documenting the struct itself**.
|
||||
|
||||
type Address struct {
|
||||
Country string `json:"country,omitempty"`
|
||||
PostCode int `json:"postcode,omitempty"`
|
||||
}
|
||||
|
||||
func (Address) SwaggerDoc() map[string]string {
|
||||
return map[string]string{
|
||||
"": "Address doc",
|
||||
"country": "Country doc",
|
||||
"postcode": "PostCode doc",
|
||||
}
|
||||
}
|
||||
|
||||
This example will generate a JSON like this
|
||||
|
||||
{
|
||||
"Address": {
|
||||
"id": "Address",
|
||||
"description": "Address doc",
|
||||
"properties": {
|
||||
"country": {
|
||||
"type": "string",
|
||||
"description": "Country doc"
|
||||
},
|
||||
"postcode": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"description": "PostCode doc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
**Very Important Notes:**
|
||||
- `SwaggerDoc()` is using a **NON-Pointer** receiver (e.g. func (Address) and not func (*Address))
|
||||
- The returned map should use as key the name of the field as defined in the JSON parameter (e.g. `"postcode"` and not `"PostCode"`)
|
||||
|
||||
Notes
|
||||
--
|
||||
- The Nickname of an Operation is automatically set by finding the name of the function. You can override it using RouteBuilder.Operation(..)
|
||||
- The WebServices field of swagger.Config can be used to control which service you want to expose and document ; you can have multiple configs and therefore multiple endpoints.
|
||||
64
vendor/github.com/emicklei/go-restful/swagger/api_declaration_list.go
generated
vendored
Normal file
64
vendor/github.com/emicklei/go-restful/swagger/api_declaration_list.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package swagger
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// ApiDeclarationList maintains an ordered list of ApiDeclaration.
|
||||
type ApiDeclarationList struct {
|
||||
List []ApiDeclaration
|
||||
}
|
||||
|
||||
// At returns the ApiDeclaration by its path unless absent, then ok is false
|
||||
func (l *ApiDeclarationList) At(path string) (a ApiDeclaration, ok bool) {
|
||||
for _, each := range l.List {
|
||||
if each.ResourcePath == path {
|
||||
return each, true
|
||||
}
|
||||
}
|
||||
return a, false
|
||||
}
|
||||
|
||||
// Put adds or replaces a ApiDeclaration with this name
|
||||
func (l *ApiDeclarationList) Put(path string, a ApiDeclaration) {
|
||||
// maybe replace existing
|
||||
for i, each := range l.List {
|
||||
if each.ResourcePath == path {
|
||||
// replace
|
||||
l.List[i] = a
|
||||
return
|
||||
}
|
||||
}
|
||||
// add
|
||||
l.List = append(l.List, a)
|
||||
}
|
||||
|
||||
// Do enumerates all the properties, each with its assigned name
|
||||
func (l *ApiDeclarationList) Do(block func(path string, decl ApiDeclaration)) {
|
||||
for _, each := range l.List {
|
||||
block(each.ResourcePath, each)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON writes the ModelPropertyList as if it was a map[string]ModelProperty
|
||||
func (l ApiDeclarationList) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
buf.WriteString("{\n")
|
||||
for i, each := range l.List {
|
||||
buf.WriteString("\"")
|
||||
buf.WriteString(each.ResourcePath)
|
||||
buf.WriteString("\": ")
|
||||
encoder.Encode(each)
|
||||
if i < len(l.List)-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
34
vendor/github.com/emicklei/go-restful/swagger/config.go
generated
vendored
Normal file
34
vendor/github.com/emicklei/go-restful/swagger/config.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
)
|
||||
|
||||
// PostBuildDeclarationMapFunc can be used to modify the api declaration map.
|
||||
type PostBuildDeclarationMapFunc func(apiDeclarationMap *ApiDeclarationList)
|
||||
|
||||
type Config struct {
|
||||
// url where the services are available, e.g. http://localhost:8080
|
||||
// if left empty then the basePath of Swagger is taken from the actual request
|
||||
WebServicesUrl string
|
||||
// path where the JSON api is avaiable , e.g. /apidocs
|
||||
ApiPath string
|
||||
// [optional] path where the swagger UI will be served, e.g. /swagger
|
||||
SwaggerPath string
|
||||
// [optional] location of folder containing Swagger HTML5 application index.html
|
||||
SwaggerFilePath string
|
||||
// api listing is constructed from this list of restful WebServices.
|
||||
WebServices []*restful.WebService
|
||||
// will serve all static content (scripts,pages,images)
|
||||
StaticHandler http.Handler
|
||||
// [optional] on default CORS (Cross-Origin-Resource-Sharing) is enabled.
|
||||
DisableCORS bool
|
||||
// Top-level API version. Is reflected in the resource listing.
|
||||
ApiVersion string
|
||||
// If set then call this handler after building the complete ApiDeclaration Map
|
||||
PostBuildHandler PostBuildDeclarationMapFunc
|
||||
// Swagger global info struct
|
||||
Info Info
|
||||
}
|
||||
430
vendor/github.com/emicklei/go-restful/swagger/model_builder.go
generated
vendored
Normal file
430
vendor/github.com/emicklei/go-restful/swagger/model_builder.go
generated
vendored
Normal file
@@ -0,0 +1,430 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ModelBuildable is used for extending Structs that need more control over
|
||||
// how the Model appears in the Swagger api declaration.
|
||||
type ModelBuildable interface {
|
||||
PostBuildModel(m *Model) *Model
|
||||
}
|
||||
|
||||
type modelBuilder struct {
|
||||
Models *ModelList
|
||||
}
|
||||
|
||||
type documentable interface {
|
||||
SwaggerDoc() map[string]string
|
||||
}
|
||||
|
||||
// Check if this structure has a method with signature func (<theModel>) SwaggerDoc() map[string]string
|
||||
// If it exists, retrive the documentation and overwrite all struct tag descriptions
|
||||
func getDocFromMethodSwaggerDoc2(model reflect.Type) map[string]string {
|
||||
if docable, ok := reflect.New(model).Elem().Interface().(documentable); ok {
|
||||
return docable.SwaggerDoc()
|
||||
}
|
||||
return make(map[string]string)
|
||||
}
|
||||
|
||||
// addModelFrom creates and adds a Model to the builder and detects and calls
|
||||
// the post build hook for customizations
|
||||
func (b modelBuilder) addModelFrom(sample interface{}) {
|
||||
if modelOrNil := b.addModel(reflect.TypeOf(sample), ""); modelOrNil != nil {
|
||||
// allow customizations
|
||||
if buildable, ok := sample.(ModelBuildable); ok {
|
||||
modelOrNil = buildable.PostBuildModel(modelOrNil)
|
||||
b.Models.Put(modelOrNil.Id, *modelOrNil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b modelBuilder) addModel(st reflect.Type, nameOverride string) *Model {
|
||||
modelName := b.keyFrom(st)
|
||||
if nameOverride != "" {
|
||||
modelName = nameOverride
|
||||
}
|
||||
// no models needed for primitive types
|
||||
if b.isPrimitiveType(modelName) {
|
||||
return nil
|
||||
}
|
||||
// see if we already have visited this model
|
||||
if _, ok := b.Models.At(modelName); ok {
|
||||
return nil
|
||||
}
|
||||
sm := Model{
|
||||
Id: modelName,
|
||||
Required: []string{},
|
||||
Properties: ModelPropertyList{}}
|
||||
|
||||
// reference the model before further initializing (enables recursive structs)
|
||||
b.Models.Put(modelName, sm)
|
||||
|
||||
// check for slice or array
|
||||
if st.Kind() == reflect.Slice || st.Kind() == reflect.Array {
|
||||
b.addModel(st.Elem(), "")
|
||||
return &sm
|
||||
}
|
||||
// check for structure or primitive type
|
||||
if st.Kind() != reflect.Struct {
|
||||
return &sm
|
||||
}
|
||||
|
||||
fullDoc := getDocFromMethodSwaggerDoc2(st)
|
||||
modelDescriptions := []string{}
|
||||
|
||||
for i := 0; i < st.NumField(); i++ {
|
||||
field := st.Field(i)
|
||||
jsonName, modelDescription, prop := b.buildProperty(field, &sm, modelName)
|
||||
if len(modelDescription) > 0 {
|
||||
modelDescriptions = append(modelDescriptions, modelDescription)
|
||||
}
|
||||
|
||||
// add if not omitted
|
||||
if len(jsonName) != 0 {
|
||||
// update description
|
||||
if fieldDoc, ok := fullDoc[jsonName]; ok {
|
||||
prop.Description = fieldDoc
|
||||
}
|
||||
// update Required
|
||||
if b.isPropertyRequired(field) {
|
||||
sm.Required = append(sm.Required, jsonName)
|
||||
}
|
||||
sm.Properties.Put(jsonName, prop)
|
||||
}
|
||||
}
|
||||
|
||||
// We always overwrite documentation if SwaggerDoc method exists
|
||||
// "" is special for documenting the struct itself
|
||||
if modelDoc, ok := fullDoc[""]; ok {
|
||||
sm.Description = modelDoc
|
||||
} else if len(modelDescriptions) != 0 {
|
||||
sm.Description = strings.Join(modelDescriptions, "\n")
|
||||
}
|
||||
|
||||
// update model builder with completed model
|
||||
b.Models.Put(modelName, sm)
|
||||
|
||||
return &sm
|
||||
}
|
||||
|
||||
func (b modelBuilder) isPropertyRequired(field reflect.StructField) bool {
|
||||
required := true
|
||||
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
|
||||
s := strings.Split(jsonTag, ",")
|
||||
if len(s) > 1 && s[1] == "omitempty" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return required
|
||||
}
|
||||
|
||||
func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, modelName string) (jsonName, modelDescription string, prop ModelProperty) {
|
||||
jsonName = b.jsonNameOfField(field)
|
||||
if len(jsonName) == 0 {
|
||||
// empty name signals skip property
|
||||
return "", "", prop
|
||||
}
|
||||
|
||||
if tag := field.Tag.Get("modelDescription"); tag != "" {
|
||||
modelDescription = tag
|
||||
}
|
||||
|
||||
prop.setPropertyMetadata(field)
|
||||
if prop.Type != nil {
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
fieldType := field.Type
|
||||
|
||||
// check if type is doing its own marshalling
|
||||
marshalerType := reflect.TypeOf((*json.Marshaler)(nil)).Elem()
|
||||
if fieldType.Implements(marshalerType) {
|
||||
var pType = "string"
|
||||
if prop.Type == nil {
|
||||
prop.Type = &pType
|
||||
}
|
||||
if prop.Format == "" {
|
||||
prop.Format = b.jsonSchemaFormat(fieldType.String())
|
||||
}
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
|
||||
// check if annotation says it is a string
|
||||
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
|
||||
s := strings.Split(jsonTag, ",")
|
||||
if len(s) > 1 && s[1] == "string" {
|
||||
stringt := "string"
|
||||
prop.Type = &stringt
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
}
|
||||
|
||||
fieldKind := fieldType.Kind()
|
||||
switch {
|
||||
case fieldKind == reflect.Struct:
|
||||
jsonName, prop := b.buildStructTypeProperty(field, jsonName, model)
|
||||
return jsonName, modelDescription, prop
|
||||
case fieldKind == reflect.Slice || fieldKind == reflect.Array:
|
||||
jsonName, prop := b.buildArrayTypeProperty(field, jsonName, modelName)
|
||||
return jsonName, modelDescription, prop
|
||||
case fieldKind == reflect.Ptr:
|
||||
jsonName, prop := b.buildPointerTypeProperty(field, jsonName, modelName)
|
||||
return jsonName, modelDescription, prop
|
||||
case fieldKind == reflect.String:
|
||||
stringt := "string"
|
||||
prop.Type = &stringt
|
||||
return jsonName, modelDescription, prop
|
||||
case fieldKind == reflect.Map:
|
||||
// if it's a map, it's unstructured, and swagger 1.2 can't handle it
|
||||
objectType := "object"
|
||||
prop.Type = &objectType
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
|
||||
if b.isPrimitiveType(fieldType.String()) {
|
||||
mapped := b.jsonSchemaType(fieldType.String())
|
||||
prop.Type = &mapped
|
||||
prop.Format = b.jsonSchemaFormat(fieldType.String())
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
modelType := fieldType.String()
|
||||
prop.Ref = &modelType
|
||||
|
||||
if fieldType.Name() == "" { // override type of anonymous structs
|
||||
nestedTypeName := modelName + "." + jsonName
|
||||
prop.Ref = &nestedTypeName
|
||||
b.addModel(fieldType, nestedTypeName)
|
||||
}
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
|
||||
func hasNamedJSONTag(field reflect.StructField) bool {
|
||||
parts := strings.Split(field.Tag.Get("json"), ",")
|
||||
if len(parts) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, s := range parts[1:] {
|
||||
if s == "inline" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return len(parts[0]) > 0
|
||||
}
|
||||
|
||||
func (b modelBuilder) buildStructTypeProperty(field reflect.StructField, jsonName string, model *Model) (nameJson string, prop ModelProperty) {
|
||||
prop.setPropertyMetadata(field)
|
||||
// Check for type override in tag
|
||||
if prop.Type != nil {
|
||||
return jsonName, prop
|
||||
}
|
||||
fieldType := field.Type
|
||||
// check for anonymous
|
||||
if len(fieldType.Name()) == 0 {
|
||||
// anonymous
|
||||
anonType := model.Id + "." + jsonName
|
||||
b.addModel(fieldType, anonType)
|
||||
prop.Ref = &anonType
|
||||
return jsonName, prop
|
||||
}
|
||||
|
||||
if field.Name == fieldType.Name() && field.Anonymous && !hasNamedJSONTag(field) {
|
||||
// embedded struct
|
||||
sub := modelBuilder{new(ModelList)}
|
||||
sub.addModel(fieldType, "")
|
||||
subKey := sub.keyFrom(fieldType)
|
||||
// merge properties from sub
|
||||
subModel, _ := sub.Models.At(subKey)
|
||||
subModel.Properties.Do(func(k string, v ModelProperty) {
|
||||
model.Properties.Put(k, v)
|
||||
// if subModel says this property is required then include it
|
||||
required := false
|
||||
for _, each := range subModel.Required {
|
||||
if k == each {
|
||||
required = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if required {
|
||||
model.Required = append(model.Required, k)
|
||||
}
|
||||
})
|
||||
// add all new referenced models
|
||||
sub.Models.Do(func(key string, sub Model) {
|
||||
if key != subKey {
|
||||
if _, ok := b.Models.At(key); !ok {
|
||||
b.Models.Put(key, sub)
|
||||
}
|
||||
}
|
||||
})
|
||||
// empty name signals skip property
|
||||
return "", prop
|
||||
}
|
||||
// simple struct
|
||||
b.addModel(fieldType, "")
|
||||
var pType = fieldType.String()
|
||||
prop.Ref = &pType
|
||||
return jsonName, prop
|
||||
}
|
||||
|
||||
func (b modelBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop ModelProperty) {
|
||||
// check for type override in tags
|
||||
prop.setPropertyMetadata(field)
|
||||
if prop.Type != nil {
|
||||
return jsonName, prop
|
||||
}
|
||||
fieldType := field.Type
|
||||
var pType = "array"
|
||||
prop.Type = &pType
|
||||
isPrimitive := b.isPrimitiveType(fieldType.Elem().Name())
|
||||
elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem())
|
||||
prop.Items = new(Item)
|
||||
if isPrimitive {
|
||||
mapped := b.jsonSchemaType(elemTypeName)
|
||||
prop.Items.Type = &mapped
|
||||
} else {
|
||||
prop.Items.Ref = &elemTypeName
|
||||
}
|
||||
// add|overwrite model for element type
|
||||
if fieldType.Elem().Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
}
|
||||
if !isPrimitive {
|
||||
b.addModel(fieldType.Elem(), elemTypeName)
|
||||
}
|
||||
return jsonName, prop
|
||||
}
|
||||
|
||||
func (b modelBuilder) buildPointerTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop ModelProperty) {
|
||||
prop.setPropertyMetadata(field)
|
||||
// Check for type override in tags
|
||||
if prop.Type != nil {
|
||||
return jsonName, prop
|
||||
}
|
||||
fieldType := field.Type
|
||||
|
||||
// override type of pointer to list-likes
|
||||
if fieldType.Elem().Kind() == reflect.Slice || fieldType.Elem().Kind() == reflect.Array {
|
||||
var pType = "array"
|
||||
prop.Type = &pType
|
||||
isPrimitive := b.isPrimitiveType(fieldType.Elem().Elem().Name())
|
||||
elemName := b.getElementTypeName(modelName, jsonName, fieldType.Elem().Elem())
|
||||
if isPrimitive {
|
||||
primName := b.jsonSchemaType(elemName)
|
||||
prop.Items = &Item{Ref: &primName}
|
||||
} else {
|
||||
prop.Items = &Item{Ref: &elemName}
|
||||
}
|
||||
if !isPrimitive {
|
||||
// add|overwrite model for element type
|
||||
b.addModel(fieldType.Elem().Elem(), elemName)
|
||||
}
|
||||
} else {
|
||||
// non-array, pointer type
|
||||
var pType = b.jsonSchemaType(fieldType.String()[1:]) // no star, include pkg path
|
||||
if b.isPrimitiveType(fieldType.String()[1:]) {
|
||||
prop.Type = &pType
|
||||
prop.Format = b.jsonSchemaFormat(fieldType.String()[1:])
|
||||
return jsonName, prop
|
||||
}
|
||||
prop.Ref = &pType
|
||||
elemName := ""
|
||||
if fieldType.Elem().Name() == "" {
|
||||
elemName = modelName + "." + jsonName
|
||||
prop.Ref = &elemName
|
||||
}
|
||||
b.addModel(fieldType.Elem(), elemName)
|
||||
}
|
||||
return jsonName, prop
|
||||
}
|
||||
|
||||
func (b modelBuilder) getElementTypeName(modelName, jsonName string, t reflect.Type) string {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
return t.String()[1:]
|
||||
}
|
||||
if t.Name() == "" {
|
||||
return modelName + "." + jsonName
|
||||
}
|
||||
return b.keyFrom(t)
|
||||
}
|
||||
|
||||
func (b modelBuilder) keyFrom(st reflect.Type) string {
|
||||
key := st.String()
|
||||
if len(st.Name()) == 0 { // unnamed type
|
||||
// Swagger UI has special meaning for [
|
||||
key = strings.Replace(key, "[]", "||", -1)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// see also https://golang.org/ref/spec#Numeric_types
|
||||
func (b modelBuilder) isPrimitiveType(modelName string) bool {
|
||||
if len(modelName) == 0 {
|
||||
return false
|
||||
}
|
||||
return strings.Contains("uint uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time", modelName)
|
||||
}
|
||||
|
||||
// jsonNameOfField returns the name of the field as it should appear in JSON format
|
||||
// An empty string indicates that this field is not part of the JSON representation
|
||||
func (b modelBuilder) jsonNameOfField(field reflect.StructField) string {
|
||||
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
|
||||
s := strings.Split(jsonTag, ",")
|
||||
if s[0] == "-" {
|
||||
// empty name signals skip property
|
||||
return ""
|
||||
} else if s[0] != "" {
|
||||
return s[0]
|
||||
}
|
||||
}
|
||||
return field.Name
|
||||
}
|
||||
|
||||
// see also http://json-schema.org/latest/json-schema-core.html#anchor8
|
||||
func (b modelBuilder) jsonSchemaType(modelName string) string {
|
||||
schemaMap := map[string]string{
|
||||
"uint": "integer",
|
||||
"uint8": "integer",
|
||||
"uint16": "integer",
|
||||
"uint32": "integer",
|
||||
"uint64": "integer",
|
||||
|
||||
"int": "integer",
|
||||
"int8": "integer",
|
||||
"int16": "integer",
|
||||
"int32": "integer",
|
||||
"int64": "integer",
|
||||
|
||||
"byte": "integer",
|
||||
"float64": "number",
|
||||
"float32": "number",
|
||||
"bool": "boolean",
|
||||
"time.Time": "string",
|
||||
}
|
||||
mapped, ok := schemaMap[modelName]
|
||||
if !ok {
|
||||
return modelName // use as is (custom or struct)
|
||||
}
|
||||
return mapped
|
||||
}
|
||||
|
||||
func (b modelBuilder) jsonSchemaFormat(modelName string) string {
|
||||
schemaMap := map[string]string{
|
||||
"int": "int32",
|
||||
"int32": "int32",
|
||||
"int64": "int64",
|
||||
"byte": "byte",
|
||||
"uint": "integer",
|
||||
"uint8": "byte",
|
||||
"float64": "double",
|
||||
"float32": "float",
|
||||
"time.Time": "date-time",
|
||||
"*time.Time": "date-time",
|
||||
}
|
||||
mapped, ok := schemaMap[modelName]
|
||||
if !ok {
|
||||
return "" // no format
|
||||
}
|
||||
return mapped
|
||||
}
|
||||
86
vendor/github.com/emicklei/go-restful/swagger/model_list.go
generated
vendored
Normal file
86
vendor/github.com/emicklei/go-restful/swagger/model_list.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
package swagger
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NamedModel associates a name with a Model (not using its Id)
|
||||
type NamedModel struct {
|
||||
Name string
|
||||
Model Model
|
||||
}
|
||||
|
||||
// ModelList encapsulates a list of NamedModel (association)
|
||||
type ModelList struct {
|
||||
List []NamedModel
|
||||
}
|
||||
|
||||
// Put adds or replaces a Model by its name
|
||||
func (l *ModelList) Put(name string, model Model) {
|
||||
for i, each := range l.List {
|
||||
if each.Name == name {
|
||||
// replace
|
||||
l.List[i] = NamedModel{name, model}
|
||||
return
|
||||
}
|
||||
}
|
||||
// add
|
||||
l.List = append(l.List, NamedModel{name, model})
|
||||
}
|
||||
|
||||
// At returns a Model by its name, ok is false if absent
|
||||
func (l *ModelList) At(name string) (m Model, ok bool) {
|
||||
for _, each := range l.List {
|
||||
if each.Name == name {
|
||||
return each.Model, true
|
||||
}
|
||||
}
|
||||
return m, false
|
||||
}
|
||||
|
||||
// Do enumerates all the models, each with its assigned name
|
||||
func (l *ModelList) Do(block func(name string, value Model)) {
|
||||
for _, each := range l.List {
|
||||
block(each.Name, each.Model)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON writes the ModelList as if it was a map[string]Model
|
||||
func (l ModelList) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
buf.WriteString("{\n")
|
||||
for i, each := range l.List {
|
||||
buf.WriteString("\"")
|
||||
buf.WriteString(each.Name)
|
||||
buf.WriteString("\": ")
|
||||
encoder.Encode(each.Model)
|
||||
if i < len(l.List)-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON reads back a ModelList. This is an expensive operation.
|
||||
func (l *ModelList) UnmarshalJSON(data []byte) error {
|
||||
raw := map[string]interface{}{}
|
||||
json.NewDecoder(bytes.NewReader(data)).Decode(&raw)
|
||||
for k, v := range raw {
|
||||
// produces JSON bytes for each value
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var m Model
|
||||
json.NewDecoder(bytes.NewReader(data)).Decode(&m)
|
||||
l.Put(k, m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
66
vendor/github.com/emicklei/go-restful/swagger/model_property_ext.go
generated
vendored
Normal file
66
vendor/github.com/emicklei/go-restful/swagger/model_property_ext.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (prop *ModelProperty) setDescription(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("description"); tag != "" {
|
||||
prop.Description = tag
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setDefaultValue(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("default"); tag != "" {
|
||||
prop.DefaultValue = Special(tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setEnumValues(field reflect.StructField) {
|
||||
// We use | to separate the enum values. This value is chosen
|
||||
// since its unlikely to be useful in actual enumeration values.
|
||||
if tag := field.Tag.Get("enum"); tag != "" {
|
||||
prop.Enum = strings.Split(tag, "|")
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setMaximum(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("maximum"); tag != "" {
|
||||
prop.Maximum = tag
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setType(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("type"); tag != "" {
|
||||
prop.Type = &tag
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setMinimum(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("minimum"); tag != "" {
|
||||
prop.Minimum = tag
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setUniqueItems(field reflect.StructField) {
|
||||
tag := field.Tag.Get("unique")
|
||||
switch tag {
|
||||
case "true":
|
||||
v := true
|
||||
prop.UniqueItems = &v
|
||||
case "false":
|
||||
v := false
|
||||
prop.UniqueItems = &v
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setPropertyMetadata(field reflect.StructField) {
|
||||
prop.setDescription(field)
|
||||
prop.setEnumValues(field)
|
||||
prop.setMinimum(field)
|
||||
prop.setMaximum(field)
|
||||
prop.setUniqueItems(field)
|
||||
prop.setDefaultValue(field)
|
||||
prop.setType(field)
|
||||
}
|
||||
87
vendor/github.com/emicklei/go-restful/swagger/model_property_list.go
generated
vendored
Normal file
87
vendor/github.com/emicklei/go-restful/swagger/model_property_list.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package swagger
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NamedModelProperty associates a name to a ModelProperty
|
||||
type NamedModelProperty struct {
|
||||
Name string
|
||||
Property ModelProperty
|
||||
}
|
||||
|
||||
// ModelPropertyList encapsulates a list of NamedModelProperty (association)
|
||||
type ModelPropertyList struct {
|
||||
List []NamedModelProperty
|
||||
}
|
||||
|
||||
// At returns the ModelPropety by its name unless absent, then ok is false
|
||||
func (l *ModelPropertyList) At(name string) (p ModelProperty, ok bool) {
|
||||
for _, each := range l.List {
|
||||
if each.Name == name {
|
||||
return each.Property, true
|
||||
}
|
||||
}
|
||||
return p, false
|
||||
}
|
||||
|
||||
// Put adds or replaces a ModelProperty with this name
|
||||
func (l *ModelPropertyList) Put(name string, prop ModelProperty) {
|
||||
// maybe replace existing
|
||||
for i, each := range l.List {
|
||||
if each.Name == name {
|
||||
// replace
|
||||
l.List[i] = NamedModelProperty{Name: name, Property: prop}
|
||||
return
|
||||
}
|
||||
}
|
||||
// add
|
||||
l.List = append(l.List, NamedModelProperty{Name: name, Property: prop})
|
||||
}
|
||||
|
||||
// Do enumerates all the properties, each with its assigned name
|
||||
func (l *ModelPropertyList) Do(block func(name string, value ModelProperty)) {
|
||||
for _, each := range l.List {
|
||||
block(each.Name, each.Property)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON writes the ModelPropertyList as if it was a map[string]ModelProperty
|
||||
func (l ModelPropertyList) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
buf.WriteString("{\n")
|
||||
for i, each := range l.List {
|
||||
buf.WriteString("\"")
|
||||
buf.WriteString(each.Name)
|
||||
buf.WriteString("\": ")
|
||||
encoder.Encode(each.Property)
|
||||
if i < len(l.List)-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON reads back a ModelPropertyList. This is an expensive operation.
|
||||
func (l *ModelPropertyList) UnmarshalJSON(data []byte) error {
|
||||
raw := map[string]interface{}{}
|
||||
json.NewDecoder(bytes.NewReader(data)).Decode(&raw)
|
||||
for k, v := range raw {
|
||||
// produces JSON bytes for each value
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var m ModelProperty
|
||||
json.NewDecoder(bytes.NewReader(data)).Decode(&m)
|
||||
l.Put(k, m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
36
vendor/github.com/emicklei/go-restful/swagger/ordered_route_map.go
generated
vendored
Normal file
36
vendor/github.com/emicklei/go-restful/swagger/ordered_route_map.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package swagger
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import "github.com/emicklei/go-restful"
|
||||
|
||||
type orderedRouteMap struct {
|
||||
elements map[string][]restful.Route
|
||||
keys []string
|
||||
}
|
||||
|
||||
func newOrderedRouteMap() *orderedRouteMap {
|
||||
return &orderedRouteMap{
|
||||
elements: map[string][]restful.Route{},
|
||||
keys: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
func (o *orderedRouteMap) Add(key string, route restful.Route) {
|
||||
routes, ok := o.elements[key]
|
||||
if ok {
|
||||
routes = append(routes, route)
|
||||
o.elements[key] = routes
|
||||
return
|
||||
}
|
||||
o.elements[key] = []restful.Route{route}
|
||||
o.keys = append(o.keys, key)
|
||||
}
|
||||
|
||||
func (o *orderedRouteMap) Do(block func(key string, routes []restful.Route)) {
|
||||
for _, k := range o.keys {
|
||||
block(k, o.elements[k])
|
||||
}
|
||||
}
|
||||
184
vendor/github.com/emicklei/go-restful/swagger/swagger.go
generated
vendored
Normal file
184
vendor/github.com/emicklei/go-restful/swagger/swagger.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
// Package swagger implements the structures of the Swagger
|
||||
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md
|
||||
package swagger
|
||||
|
||||
const swaggerVersion = "1.2"
|
||||
|
||||
// 4.3.3 Data Type Fields
|
||||
type DataTypeFields struct {
|
||||
Type *string `json:"type,omitempty"` // if Ref not used
|
||||
Ref *string `json:"$ref,omitempty"` // if Type not used
|
||||
Format string `json:"format,omitempty"`
|
||||
DefaultValue Special `json:"defaultValue,omitempty"`
|
||||
Enum []string `json:"enum,omitempty"`
|
||||
Minimum string `json:"minimum,omitempty"`
|
||||
Maximum string `json:"maximum,omitempty"`
|
||||
Items *Item `json:"items,omitempty"`
|
||||
UniqueItems *bool `json:"uniqueItems,omitempty"`
|
||||
}
|
||||
|
||||
type Special string
|
||||
|
||||
// 4.3.4 Items Object
|
||||
type Item struct {
|
||||
Type *string `json:"type,omitempty"`
|
||||
Ref *string `json:"$ref,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
}
|
||||
|
||||
// 5.1 Resource Listing
|
||||
type ResourceListing struct {
|
||||
SwaggerVersion string `json:"swaggerVersion"` // e.g 1.2
|
||||
Apis []Resource `json:"apis"`
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
Info Info `json:"info"`
|
||||
Authorizations []Authorization `json:"authorizations,omitempty"`
|
||||
}
|
||||
|
||||
// 5.1.2 Resource Object
|
||||
type Resource struct {
|
||||
Path string `json:"path"` // relative or absolute, must start with /
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// 5.1.3 Info Object
|
||||
type Info struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
TermsOfServiceUrl string `json:"termsOfServiceUrl,omitempty"`
|
||||
Contact string `json:"contact,omitempty"`
|
||||
License string `json:"license,omitempty"`
|
||||
LicenseUrl string `json:"licenseUrl,omitempty"`
|
||||
}
|
||||
|
||||
// 5.1.5
|
||||
type Authorization struct {
|
||||
Type string `json:"type"`
|
||||
PassAs string `json:"passAs"`
|
||||
Keyname string `json:"keyname"`
|
||||
Scopes []Scope `json:"scopes"`
|
||||
GrantTypes []GrantType `json:"grandTypes"`
|
||||
}
|
||||
|
||||
// 5.1.6, 5.2.11
|
||||
type Scope struct {
|
||||
// Required. The name of the scope.
|
||||
Scope string `json:"scope"`
|
||||
// Recommended. A short description of the scope.
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// 5.1.7
|
||||
type GrantType struct {
|
||||
Implicit Implicit `json:"implicit"`
|
||||
AuthorizationCode AuthorizationCode `json:"authorization_code"`
|
||||
}
|
||||
|
||||
// 5.1.8 Implicit Object
|
||||
type Implicit struct {
|
||||
// Required. The login endpoint definition.
|
||||
loginEndpoint LoginEndpoint `json:"loginEndpoint"`
|
||||
// An optional alternative name to standard "access_token" OAuth2 parameter.
|
||||
TokenName string `json:"tokenName"`
|
||||
}
|
||||
|
||||
// 5.1.9 Authorization Code Object
|
||||
type AuthorizationCode struct {
|
||||
TokenRequestEndpoint TokenRequestEndpoint `json:"tokenRequestEndpoint"`
|
||||
TokenEndpoint TokenEndpoint `json:"tokenEndpoint"`
|
||||
}
|
||||
|
||||
// 5.1.10 Login Endpoint Object
|
||||
type LoginEndpoint struct {
|
||||
// Required. The URL of the authorization endpoint for the implicit grant flow. The value SHOULD be in a URL format.
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
// 5.1.11 Token Request Endpoint Object
|
||||
type TokenRequestEndpoint struct {
|
||||
// Required. The URL of the authorization endpoint for the authentication code grant flow. The value SHOULD be in a URL format.
|
||||
Url string `json:"url"`
|
||||
// An optional alternative name to standard "client_id" OAuth2 parameter.
|
||||
ClientIdName string `json:"clientIdName"`
|
||||
// An optional alternative name to the standard "client_secret" OAuth2 parameter.
|
||||
ClientSecretName string `json:"clientSecretName"`
|
||||
}
|
||||
|
||||
// 5.1.12 Token Endpoint Object
|
||||
type TokenEndpoint struct {
|
||||
// Required. The URL of the token endpoint for the authentication code grant flow. The value SHOULD be in a URL format.
|
||||
Url string `json:"url"`
|
||||
// An optional alternative name to standard "access_token" OAuth2 parameter.
|
||||
TokenName string `json:"tokenName"`
|
||||
}
|
||||
|
||||
// 5.2 API Declaration
|
||||
type ApiDeclaration struct {
|
||||
SwaggerVersion string `json:"swaggerVersion"`
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
BasePath string `json:"basePath"`
|
||||
ResourcePath string `json:"resourcePath"` // must start with /
|
||||
Apis []Api `json:"apis,omitempty"`
|
||||
Models ModelList `json:"models,omitempty"`
|
||||
Produces []string `json:"produces,omitempty"`
|
||||
Consumes []string `json:"consumes,omitempty"`
|
||||
Authorizations []Authorization `json:"authorizations,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.2 API Object
|
||||
type Api struct {
|
||||
Path string `json:"path"` // relative or absolute, must start with /
|
||||
Description string `json:"description"`
|
||||
Operations []Operation `json:"operations,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.3 Operation Object
|
||||
type Operation struct {
|
||||
DataTypeFields
|
||||
Method string `json:"method"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Notes string `json:"notes,omitempty"`
|
||||
Nickname string `json:"nickname"`
|
||||
Authorizations []Authorization `json:"authorizations,omitempty"`
|
||||
Parameters []Parameter `json:"parameters"`
|
||||
ResponseMessages []ResponseMessage `json:"responseMessages,omitempty"` // optional
|
||||
Produces []string `json:"produces,omitempty"`
|
||||
Consumes []string `json:"consumes,omitempty"`
|
||||
Deprecated string `json:"deprecated,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.4 Parameter Object
|
||||
type Parameter struct {
|
||||
DataTypeFields
|
||||
ParamType string `json:"paramType"` // path,query,body,header,form
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Required bool `json:"required"`
|
||||
AllowMultiple bool `json:"allowMultiple"`
|
||||
}
|
||||
|
||||
// 5.2.5 Response Message Object
|
||||
type ResponseMessage struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
ResponseModel string `json:"responseModel,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.6, 5.2.7 Models Object
|
||||
type Model struct {
|
||||
Id string `json:"id"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Required []string `json:"required,omitempty"`
|
||||
Properties ModelPropertyList `json:"properties"`
|
||||
SubTypes []string `json:"subTypes,omitempty"`
|
||||
Discriminator string `json:"discriminator,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.8 Properties Object
|
||||
type ModelProperty struct {
|
||||
DataTypeFields
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.10
|
||||
type Authorizations map[string]Authorization
|
||||
21
vendor/github.com/emicklei/go-restful/swagger/swagger_builder.go
generated
vendored
Normal file
21
vendor/github.com/emicklei/go-restful/swagger/swagger_builder.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package swagger
|
||||
|
||||
type SwaggerBuilder struct {
|
||||
SwaggerService
|
||||
}
|
||||
|
||||
func NewSwaggerBuilder(config Config) *SwaggerBuilder {
|
||||
return &SwaggerBuilder{*newSwaggerService(config)}
|
||||
}
|
||||
|
||||
func (sb SwaggerBuilder) ProduceListing() ResourceListing {
|
||||
return sb.SwaggerService.produceListing()
|
||||
}
|
||||
|
||||
func (sb SwaggerBuilder) ProduceAllDeclarations() map[string]ApiDeclaration {
|
||||
return sb.SwaggerService.produceAllDeclarations()
|
||||
}
|
||||
|
||||
func (sb SwaggerBuilder) ProduceDeclarations(route string) (*ApiDeclaration, bool) {
|
||||
return sb.SwaggerService.produceDeclarations(route)
|
||||
}
|
||||
440
vendor/github.com/emicklei/go-restful/swagger/swagger_webservice.go
generated
vendored
Normal file
440
vendor/github.com/emicklei/go-restful/swagger/swagger_webservice.go
generated
vendored
Normal file
@@ -0,0 +1,440 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
// "github.com/emicklei/hopwatch"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful/log"
|
||||
)
|
||||
|
||||
type SwaggerService struct {
|
||||
config Config
|
||||
apiDeclarationMap *ApiDeclarationList
|
||||
}
|
||||
|
||||
func newSwaggerService(config Config) *SwaggerService {
|
||||
sws := &SwaggerService{
|
||||
config: config,
|
||||
apiDeclarationMap: new(ApiDeclarationList)}
|
||||
|
||||
// Build all ApiDeclarations
|
||||
for _, each := range config.WebServices {
|
||||
rootPath := each.RootPath()
|
||||
// skip the api service itself
|
||||
if rootPath != config.ApiPath {
|
||||
if rootPath == "" || rootPath == "/" {
|
||||
// use routes
|
||||
for _, route := range each.Routes() {
|
||||
entry := staticPathFromRoute(route)
|
||||
_, exists := sws.apiDeclarationMap.At(entry)
|
||||
if !exists {
|
||||
sws.apiDeclarationMap.Put(entry, sws.composeDeclaration(each, entry))
|
||||
}
|
||||
}
|
||||
} else { // use root path
|
||||
sws.apiDeclarationMap.Put(each.RootPath(), sws.composeDeclaration(each, each.RootPath()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if specified then call the PostBuilderHandler
|
||||
if config.PostBuildHandler != nil {
|
||||
config.PostBuildHandler(sws.apiDeclarationMap)
|
||||
}
|
||||
return sws
|
||||
}
|
||||
|
||||
// LogInfo is the function that is called when this package needs to log. It defaults to log.Printf
|
||||
var LogInfo = func(format string, v ...interface{}) {
|
||||
// use the restful package-wide logger
|
||||
log.Printf(format, v...)
|
||||
}
|
||||
|
||||
// InstallSwaggerService add the WebService that provides the API documentation of all services
|
||||
// conform the Swagger documentation specifcation. (https://github.com/wordnik/swagger-core/wiki).
|
||||
func InstallSwaggerService(aSwaggerConfig Config) {
|
||||
RegisterSwaggerService(aSwaggerConfig, restful.DefaultContainer)
|
||||
}
|
||||
|
||||
// RegisterSwaggerService add the WebService that provides the API documentation of all services
|
||||
// conform the Swagger documentation specifcation. (https://github.com/wordnik/swagger-core/wiki).
|
||||
func RegisterSwaggerService(config Config, wsContainer *restful.Container) {
|
||||
sws := newSwaggerService(config)
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(config.ApiPath)
|
||||
ws.Produces(restful.MIME_JSON)
|
||||
if config.DisableCORS {
|
||||
ws.Filter(enableCORS)
|
||||
}
|
||||
ws.Route(ws.GET("/").To(sws.getListing))
|
||||
ws.Route(ws.GET("/{a}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}/{d}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}/{d}/{e}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}/{d}/{e}/{f}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}/{d}/{e}/{f}/{g}").To(sws.getDeclarations))
|
||||
LogInfo("[restful/swagger] listing is available at %v%v", config.WebServicesUrl, config.ApiPath)
|
||||
wsContainer.Add(ws)
|
||||
|
||||
// Check paths for UI serving
|
||||
if config.StaticHandler == nil && config.SwaggerFilePath != "" && config.SwaggerPath != "" {
|
||||
swaggerPathSlash := config.SwaggerPath
|
||||
// path must end with slash /
|
||||
if "/" != config.SwaggerPath[len(config.SwaggerPath)-1:] {
|
||||
LogInfo("[restful/swagger] use corrected SwaggerPath ; must end with slash (/)")
|
||||
swaggerPathSlash += "/"
|
||||
}
|
||||
|
||||
LogInfo("[restful/swagger] %v%v is mapped to folder %v", config.WebServicesUrl, swaggerPathSlash, config.SwaggerFilePath)
|
||||
wsContainer.Handle(swaggerPathSlash, http.StripPrefix(swaggerPathSlash, http.FileServer(http.Dir(config.SwaggerFilePath))))
|
||||
|
||||
//if we define a custom static handler use it
|
||||
} else if config.StaticHandler != nil && config.SwaggerPath != "" {
|
||||
swaggerPathSlash := config.SwaggerPath
|
||||
// path must end with slash /
|
||||
if "/" != config.SwaggerPath[len(config.SwaggerPath)-1:] {
|
||||
LogInfo("[restful/swagger] use corrected SwaggerFilePath ; must end with slash (/)")
|
||||
swaggerPathSlash += "/"
|
||||
|
||||
}
|
||||
LogInfo("[restful/swagger] %v%v is mapped to custom Handler %T", config.WebServicesUrl, swaggerPathSlash, config.StaticHandler)
|
||||
wsContainer.Handle(swaggerPathSlash, config.StaticHandler)
|
||||
|
||||
} else {
|
||||
LogInfo("[restful/swagger] Swagger(File)Path is empty ; no UI is served")
|
||||
}
|
||||
}
|
||||
|
||||
func staticPathFromRoute(r restful.Route) string {
|
||||
static := r.Path
|
||||
bracket := strings.Index(static, "{")
|
||||
if bracket <= 1 { // result cannot be empty
|
||||
return static
|
||||
}
|
||||
if bracket != -1 {
|
||||
static = r.Path[:bracket]
|
||||
}
|
||||
if strings.HasSuffix(static, "/") {
|
||||
return static[:len(static)-1]
|
||||
} else {
|
||||
return static
|
||||
}
|
||||
}
|
||||
|
||||
func enableCORS(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
|
||||
if origin := req.HeaderParameter(restful.HEADER_Origin); origin != "" {
|
||||
// prevent duplicate header
|
||||
if len(resp.Header().Get(restful.HEADER_AccessControlAllowOrigin)) == 0 {
|
||||
resp.AddHeader(restful.HEADER_AccessControlAllowOrigin, origin)
|
||||
}
|
||||
}
|
||||
chain.ProcessFilter(req, resp)
|
||||
}
|
||||
|
||||
func (sws SwaggerService) getListing(req *restful.Request, resp *restful.Response) {
|
||||
listing := sws.produceListing()
|
||||
resp.WriteAsJson(listing)
|
||||
}
|
||||
|
||||
func (sws SwaggerService) produceListing() ResourceListing {
|
||||
listing := ResourceListing{SwaggerVersion: swaggerVersion, ApiVersion: sws.config.ApiVersion, Info: sws.config.Info}
|
||||
sws.apiDeclarationMap.Do(func(k string, v ApiDeclaration) {
|
||||
ref := Resource{Path: k}
|
||||
if len(v.Apis) > 0 { // use description of first (could still be empty)
|
||||
ref.Description = v.Apis[0].Description
|
||||
}
|
||||
listing.Apis = append(listing.Apis, ref)
|
||||
})
|
||||
return listing
|
||||
}
|
||||
|
||||
func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Response) {
|
||||
decl, ok := sws.produceDeclarations(composeRootPath(req))
|
||||
if !ok {
|
||||
resp.WriteErrorString(http.StatusNotFound, "ApiDeclaration not found")
|
||||
return
|
||||
}
|
||||
// unless WebServicesUrl is given
|
||||
if len(sws.config.WebServicesUrl) == 0 {
|
||||
// update base path from the actual request
|
||||
// TODO how to detect https? assume http for now
|
||||
var host string
|
||||
// X-Forwarded-Host or Host or Request.Host
|
||||
hostvalues, ok := req.Request.Header["X-Forwarded-Host"] // apache specific?
|
||||
if !ok || len(hostvalues) == 0 {
|
||||
forwarded, ok := req.Request.Header["Host"] // without reverse-proxy
|
||||
if !ok || len(forwarded) == 0 {
|
||||
// fallback to Host field
|
||||
host = req.Request.Host
|
||||
} else {
|
||||
host = forwarded[0]
|
||||
}
|
||||
} else {
|
||||
host = hostvalues[0]
|
||||
}
|
||||
// inspect Referer for the scheme (http vs https)
|
||||
scheme := "http"
|
||||
if referer := req.Request.Header["Referer"]; len(referer) > 0 {
|
||||
if strings.HasPrefix(referer[0], "https") {
|
||||
scheme = "https"
|
||||
}
|
||||
}
|
||||
decl.BasePath = fmt.Sprintf("%s://%s", scheme, host)
|
||||
}
|
||||
resp.WriteAsJson(decl)
|
||||
}
|
||||
|
||||
func (sws SwaggerService) produceAllDeclarations() map[string]ApiDeclaration {
|
||||
decls := map[string]ApiDeclaration{}
|
||||
sws.apiDeclarationMap.Do(func(k string, v ApiDeclaration) {
|
||||
decls[k] = v
|
||||
})
|
||||
return decls
|
||||
}
|
||||
|
||||
func (sws SwaggerService) produceDeclarations(route string) (*ApiDeclaration, bool) {
|
||||
decl, ok := sws.apiDeclarationMap.At(route)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
decl.BasePath = sws.config.WebServicesUrl
|
||||
return &decl, true
|
||||
}
|
||||
|
||||
// composeDeclaration uses all routes and parameters to create a ApiDeclaration
|
||||
func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix string) ApiDeclaration {
|
||||
decl := ApiDeclaration{
|
||||
SwaggerVersion: swaggerVersion,
|
||||
BasePath: sws.config.WebServicesUrl,
|
||||
ResourcePath: pathPrefix,
|
||||
Models: ModelList{},
|
||||
ApiVersion: ws.Version()}
|
||||
|
||||
// collect any path parameters
|
||||
rootParams := []Parameter{}
|
||||
for _, param := range ws.PathParameters() {
|
||||
rootParams = append(rootParams, asSwaggerParameter(param.Data()))
|
||||
}
|
||||
// aggregate by path
|
||||
pathToRoutes := newOrderedRouteMap()
|
||||
for _, other := range ws.Routes() {
|
||||
if strings.HasPrefix(other.Path, pathPrefix) {
|
||||
pathToRoutes.Add(other.Path, other)
|
||||
}
|
||||
}
|
||||
pathToRoutes.Do(func(path string, routes []restful.Route) {
|
||||
api := Api{Path: strings.TrimSuffix(withoutWildcard(path), "/"), Description: ws.Documentation()}
|
||||
voidString := "void"
|
||||
for _, route := range routes {
|
||||
operation := Operation{
|
||||
Method: route.Method,
|
||||
Summary: route.Doc,
|
||||
Notes: route.Notes,
|
||||
// Type gets overwritten if there is a write sample
|
||||
DataTypeFields: DataTypeFields{Type: &voidString},
|
||||
Parameters: []Parameter{},
|
||||
Nickname: route.Operation,
|
||||
ResponseMessages: composeResponseMessages(route, &decl)}
|
||||
|
||||
operation.Consumes = route.Consumes
|
||||
operation.Produces = route.Produces
|
||||
|
||||
// share root params if any
|
||||
for _, swparam := range rootParams {
|
||||
operation.Parameters = append(operation.Parameters, swparam)
|
||||
}
|
||||
// route specific params
|
||||
for _, param := range route.ParameterDocs {
|
||||
operation.Parameters = append(operation.Parameters, asSwaggerParameter(param.Data()))
|
||||
}
|
||||
|
||||
sws.addModelsFromRouteTo(&operation, route, &decl)
|
||||
api.Operations = append(api.Operations, operation)
|
||||
}
|
||||
decl.Apis = append(decl.Apis, api)
|
||||
})
|
||||
return decl
|
||||
}
|
||||
|
||||
func withoutWildcard(path string) string {
|
||||
if strings.HasSuffix(path, ":*}") {
|
||||
return path[0:len(path)-3] + "}"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// composeResponseMessages takes the ResponseErrors (if any) and creates ResponseMessages from them.
|
||||
func composeResponseMessages(route restful.Route, decl *ApiDeclaration) (messages []ResponseMessage) {
|
||||
if route.ResponseErrors == nil {
|
||||
return messages
|
||||
}
|
||||
// sort by code
|
||||
codes := sort.IntSlice{}
|
||||
for code, _ := range route.ResponseErrors {
|
||||
codes = append(codes, code)
|
||||
}
|
||||
codes.Sort()
|
||||
for _, code := range codes {
|
||||
each := route.ResponseErrors[code]
|
||||
message := ResponseMessage{
|
||||
Code: code,
|
||||
Message: each.Message,
|
||||
}
|
||||
if each.Model != nil {
|
||||
st := reflect.TypeOf(each.Model)
|
||||
isCollection, st := detectCollectionType(st)
|
||||
modelName := modelBuilder{}.keyFrom(st)
|
||||
if isCollection {
|
||||
modelName = "array[" + modelName + "]"
|
||||
}
|
||||
modelBuilder{&decl.Models}.addModel(st, "")
|
||||
// reference the model
|
||||
message.ResponseModel = modelName
|
||||
}
|
||||
messages = append(messages, message)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// addModelsFromRoute takes any read or write sample from the Route and creates a Swagger model from it.
|
||||
func (sws SwaggerService) addModelsFromRouteTo(operation *Operation, route restful.Route, decl *ApiDeclaration) {
|
||||
if route.ReadSample != nil {
|
||||
sws.addModelFromSampleTo(operation, false, route.ReadSample, &decl.Models)
|
||||
}
|
||||
if route.WriteSample != nil {
|
||||
sws.addModelFromSampleTo(operation, true, route.WriteSample, &decl.Models)
|
||||
}
|
||||
}
|
||||
|
||||
func detectCollectionType(st reflect.Type) (bool, reflect.Type) {
|
||||
isCollection := false
|
||||
if st.Kind() == reflect.Slice || st.Kind() == reflect.Array {
|
||||
st = st.Elem()
|
||||
isCollection = true
|
||||
} else {
|
||||
if st.Kind() == reflect.Ptr {
|
||||
if st.Elem().Kind() == reflect.Slice || st.Elem().Kind() == reflect.Array {
|
||||
st = st.Elem().Elem()
|
||||
isCollection = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return isCollection, st
|
||||
}
|
||||
|
||||
// addModelFromSample creates and adds (or overwrites) a Model from a sample resource
|
||||
func (sws SwaggerService) addModelFromSampleTo(operation *Operation, isResponse bool, sample interface{}, models *ModelList) {
|
||||
if isResponse {
|
||||
type_, items := asDataType(sample)
|
||||
operation.Type = type_
|
||||
operation.Items = items
|
||||
}
|
||||
modelBuilder{models}.addModelFrom(sample)
|
||||
}
|
||||
|
||||
func asSwaggerParameter(param restful.ParameterData) Parameter {
|
||||
return Parameter{
|
||||
DataTypeFields: DataTypeFields{
|
||||
Type: ¶m.DataType,
|
||||
Format: asFormat(param.DataType, param.DataFormat),
|
||||
DefaultValue: Special(param.DefaultValue),
|
||||
},
|
||||
Name: param.Name,
|
||||
Description: param.Description,
|
||||
ParamType: asParamType(param.Kind),
|
||||
|
||||
Required: param.Required}
|
||||
}
|
||||
|
||||
// Between 1..7 path parameters is supported
|
||||
func composeRootPath(req *restful.Request) string {
|
||||
path := "/" + req.PathParameter("a")
|
||||
b := req.PathParameter("b")
|
||||
if b == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + b
|
||||
c := req.PathParameter("c")
|
||||
if c == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + c
|
||||
d := req.PathParameter("d")
|
||||
if d == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + d
|
||||
e := req.PathParameter("e")
|
||||
if e == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + e
|
||||
f := req.PathParameter("f")
|
||||
if f == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + f
|
||||
g := req.PathParameter("g")
|
||||
if g == "" {
|
||||
return path
|
||||
}
|
||||
return path + "/" + g
|
||||
}
|
||||
|
||||
func asFormat(dataType string, dataFormat string) string {
|
||||
if dataFormat != "" {
|
||||
return dataFormat
|
||||
}
|
||||
return "" // TODO
|
||||
}
|
||||
|
||||
func asParamType(kind int) string {
|
||||
switch {
|
||||
case kind == restful.PathParameterKind:
|
||||
return "path"
|
||||
case kind == restful.QueryParameterKind:
|
||||
return "query"
|
||||
case kind == restful.BodyParameterKind:
|
||||
return "body"
|
||||
case kind == restful.HeaderParameterKind:
|
||||
return "header"
|
||||
case kind == restful.FormParameterKind:
|
||||
return "form"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func asDataType(any interface{}) (*string, *Item) {
|
||||
// If it's not a collection, return the suggested model name
|
||||
st := reflect.TypeOf(any)
|
||||
isCollection, st := detectCollectionType(st)
|
||||
modelName := modelBuilder{}.keyFrom(st)
|
||||
// if it's not a collection we are done
|
||||
if !isCollection {
|
||||
return &modelName, nil
|
||||
}
|
||||
|
||||
// XXX: This is not very elegant
|
||||
// We create an Item object referring to the given model
|
||||
models := ModelList{}
|
||||
mb := modelBuilder{&models}
|
||||
mb.addModelFrom(any)
|
||||
|
||||
elemTypeName := mb.getElementTypeName(modelName, "", st)
|
||||
item := new(Item)
|
||||
if mb.isPrimitiveType(elemTypeName) {
|
||||
mapped := mb.jsonSchemaType(elemTypeName)
|
||||
item.Type = &mapped
|
||||
} else {
|
||||
item.Ref = &elemTypeName
|
||||
}
|
||||
tmp := "array"
|
||||
return &tmp, item
|
||||
}
|
||||
268
vendor/github.com/emicklei/go-restful/web_service.go
generated
vendored
Normal file
268
vendor/github.com/emicklei/go-restful/web_service.go
generated
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
package restful
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/emicklei/go-restful/log"
|
||||
)
|
||||
|
||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// WebService holds a collection of Route values that bind a Http Method + URL Path to a function.
|
||||
type WebService struct {
|
||||
rootPath string
|
||||
pathExpr *pathExpression // cached compilation of rootPath as RegExp
|
||||
routes []Route
|
||||
produces []string
|
||||
consumes []string
|
||||
pathParameters []*Parameter
|
||||
filters []FilterFunction
|
||||
documentation string
|
||||
apiVersion string
|
||||
|
||||
dynamicRoutes bool
|
||||
|
||||
// protects 'routes' if dynamic routes are enabled
|
||||
routesLock sync.RWMutex
|
||||
}
|
||||
|
||||
func (w *WebService) SetDynamicRoutes(enable bool) {
|
||||
w.dynamicRoutes = enable
|
||||
}
|
||||
|
||||
// compilePathExpression ensures that the path is compiled into a RegEx for those routers that need it.
|
||||
func (w *WebService) compilePathExpression() {
|
||||
if len(w.rootPath) == 0 {
|
||||
w.Path("/") // lazy initialize path
|
||||
}
|
||||
compiled, err := newPathExpression(w.rootPath)
|
||||
if err != nil {
|
||||
log.Printf("[restful] invalid path:%s because:%v", w.rootPath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
w.pathExpr = compiled
|
||||
}
|
||||
|
||||
// ApiVersion sets the API version for documentation purposes.
|
||||
func (w *WebService) ApiVersion(apiVersion string) *WebService {
|
||||
w.apiVersion = apiVersion
|
||||
return w
|
||||
}
|
||||
|
||||
// Version returns the API version for documentation purposes.
|
||||
func (w WebService) Version() string { return w.apiVersion }
|
||||
|
||||
// Path specifies the root URL template path of the WebService.
|
||||
// All Routes will be relative to this path.
|
||||
func (w *WebService) Path(root string) *WebService {
|
||||
w.rootPath = root
|
||||
w.compilePathExpression()
|
||||
return w
|
||||
}
|
||||
|
||||
// Param adds a PathParameter to document parameters used in the root path.
|
||||
func (w *WebService) Param(parameter *Parameter) *WebService {
|
||||
if w.pathParameters == nil {
|
||||
w.pathParameters = []*Parameter{}
|
||||
}
|
||||
w.pathParameters = append(w.pathParameters, parameter)
|
||||
return w
|
||||
}
|
||||
|
||||
// PathParameter creates a new Parameter of kind Path for documentation purposes.
|
||||
// It is initialized as required with string as its DataType.
|
||||
func (w *WebService) PathParameter(name, description string) *Parameter {
|
||||
return PathParameter(name, description)
|
||||
}
|
||||
|
||||
// PathParameter creates a new Parameter of kind Path for documentation purposes.
|
||||
// It is initialized as required with string as its DataType.
|
||||
func PathParameter(name, description string) *Parameter {
|
||||
p := &Parameter{&ParameterData{Name: name, Description: description, Required: true, DataType: "string"}}
|
||||
p.bePath()
|
||||
return p
|
||||
}
|
||||
|
||||
// QueryParameter creates a new Parameter of kind Query for documentation purposes.
|
||||
// It is initialized as not required with string as its DataType.
|
||||
func (w *WebService) QueryParameter(name, description string) *Parameter {
|
||||
return QueryParameter(name, description)
|
||||
}
|
||||
|
||||
// QueryParameter creates a new Parameter of kind Query for documentation purposes.
|
||||
// It is initialized as not required with string as its DataType.
|
||||
func QueryParameter(name, description string) *Parameter {
|
||||
p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
|
||||
p.beQuery()
|
||||
return p
|
||||
}
|
||||
|
||||
// BodyParameter creates a new Parameter of kind Body for documentation purposes.
|
||||
// It is initialized as required without a DataType.
|
||||
func (w *WebService) BodyParameter(name, description string) *Parameter {
|
||||
return BodyParameter(name, description)
|
||||
}
|
||||
|
||||
// BodyParameter creates a new Parameter of kind Body for documentation purposes.
|
||||
// It is initialized as required without a DataType.
|
||||
func BodyParameter(name, description string) *Parameter {
|
||||
p := &Parameter{&ParameterData{Name: name, Description: description, Required: true}}
|
||||
p.beBody()
|
||||
return p
|
||||
}
|
||||
|
||||
// HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes.
|
||||
// It is initialized as not required with string as its DataType.
|
||||
func (w *WebService) HeaderParameter(name, description string) *Parameter {
|
||||
return HeaderParameter(name, description)
|
||||
}
|
||||
|
||||
// HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes.
|
||||
// It is initialized as not required with string as its DataType.
|
||||
func HeaderParameter(name, description string) *Parameter {
|
||||
p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
|
||||
p.beHeader()
|
||||
return p
|
||||
}
|
||||
|
||||
// FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes.
|
||||
// It is initialized as required with string as its DataType.
|
||||
func (w *WebService) FormParameter(name, description string) *Parameter {
|
||||
return FormParameter(name, description)
|
||||
}
|
||||
|
||||
// FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes.
|
||||
// It is initialized as required with string as its DataType.
|
||||
func FormParameter(name, description string) *Parameter {
|
||||
p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
|
||||
p.beForm()
|
||||
return p
|
||||
}
|
||||
|
||||
// Route creates a new Route using the RouteBuilder and add to the ordered list of Routes.
|
||||
func (w *WebService) Route(builder *RouteBuilder) *WebService {
|
||||
w.routesLock.Lock()
|
||||
defer w.routesLock.Unlock()
|
||||
builder.copyDefaults(w.produces, w.consumes)
|
||||
w.routes = append(w.routes, builder.Build())
|
||||
return w
|
||||
}
|
||||
|
||||
// RemoveRoute removes the specified route, looks for something that matches 'path' and 'method'
|
||||
func (w *WebService) RemoveRoute(path, method string) error {
|
||||
if !w.dynamicRoutes {
|
||||
return fmt.Errorf("dynamic routes are not enabled.")
|
||||
}
|
||||
w.routesLock.Lock()
|
||||
defer w.routesLock.Unlock()
|
||||
newRoutes := make([]Route, (len(w.routes) - 1))
|
||||
current := 0
|
||||
for ix := range w.routes {
|
||||
if w.routes[ix].Method == method && w.routes[ix].Path == path {
|
||||
continue
|
||||
}
|
||||
newRoutes[current] = w.routes[ix]
|
||||
current = current + 1
|
||||
}
|
||||
w.routes = newRoutes
|
||||
return nil
|
||||
}
|
||||
|
||||
// Method creates a new RouteBuilder and initialize its http method
|
||||
func (w *WebService) Method(httpMethod string) *RouteBuilder {
|
||||
return new(RouteBuilder).servicePath(w.rootPath).Method(httpMethod)
|
||||
}
|
||||
|
||||
// Produces specifies that this WebService can produce one or more MIME types.
|
||||
// Http requests must have one of these values set for the Accept header.
|
||||
func (w *WebService) Produces(contentTypes ...string) *WebService {
|
||||
w.produces = contentTypes
|
||||
return w
|
||||
}
|
||||
|
||||
// Consumes specifies that this WebService can consume one or more MIME types.
|
||||
// Http requests must have one of these values set for the Content-Type header.
|
||||
func (w *WebService) Consumes(accepts ...string) *WebService {
|
||||
w.consumes = accepts
|
||||
return w
|
||||
}
|
||||
|
||||
// Routes returns the Routes associated with this WebService
|
||||
func (w WebService) Routes() []Route {
|
||||
if !w.dynamicRoutes {
|
||||
return w.routes
|
||||
}
|
||||
// Make a copy of the array to prevent concurrency problems
|
||||
w.routesLock.RLock()
|
||||
defer w.routesLock.RUnlock()
|
||||
result := make([]Route, len(w.routes))
|
||||
for ix := range w.routes {
|
||||
result[ix] = w.routes[ix]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// RootPath returns the RootPath associated with this WebService. Default "/"
|
||||
func (w WebService) RootPath() string {
|
||||
return w.rootPath
|
||||
}
|
||||
|
||||
// PathParameters return the path parameter names for (shared amoung its Routes)
|
||||
func (w WebService) PathParameters() []*Parameter {
|
||||
return w.pathParameters
|
||||
}
|
||||
|
||||
// Filter adds a filter function to the chain of filters applicable to all its Routes
|
||||
func (w *WebService) Filter(filter FilterFunction) *WebService {
|
||||
w.filters = append(w.filters, filter)
|
||||
return w
|
||||
}
|
||||
|
||||
// Doc is used to set the documentation of this service.
|
||||
func (w *WebService) Doc(plainText string) *WebService {
|
||||
w.documentation = plainText
|
||||
return w
|
||||
}
|
||||
|
||||
// Documentation returns it.
|
||||
func (w WebService) Documentation() string {
|
||||
return w.documentation
|
||||
}
|
||||
|
||||
/*
|
||||
Convenience methods
|
||||
*/
|
||||
|
||||
// HEAD is a shortcut for .Method("HEAD").Path(subPath)
|
||||
func (w *WebService) HEAD(subPath string) *RouteBuilder {
|
||||
return new(RouteBuilder).servicePath(w.rootPath).Method("HEAD").Path(subPath)
|
||||
}
|
||||
|
||||
// GET is a shortcut for .Method("GET").Path(subPath)
|
||||
func (w *WebService) GET(subPath string) *RouteBuilder {
|
||||
return new(RouteBuilder).servicePath(w.rootPath).Method("GET").Path(subPath)
|
||||
}
|
||||
|
||||
// POST is a shortcut for .Method("POST").Path(subPath)
|
||||
func (w *WebService) POST(subPath string) *RouteBuilder {
|
||||
return new(RouteBuilder).servicePath(w.rootPath).Method("POST").Path(subPath)
|
||||
}
|
||||
|
||||
// PUT is a shortcut for .Method("PUT").Path(subPath)
|
||||
func (w *WebService) PUT(subPath string) *RouteBuilder {
|
||||
return new(RouteBuilder).servicePath(w.rootPath).Method("PUT").Path(subPath)
|
||||
}
|
||||
|
||||
// PATCH is a shortcut for .Method("PATCH").Path(subPath)
|
||||
func (w *WebService) PATCH(subPath string) *RouteBuilder {
|
||||
return new(RouteBuilder).servicePath(w.rootPath).Method("PATCH").Path(subPath)
|
||||
}
|
||||
|
||||
// DELETE is a shortcut for .Method("DELETE").Path(subPath)
|
||||
func (w *WebService) DELETE(subPath string) *RouteBuilder {
|
||||
return new(RouteBuilder).servicePath(w.rootPath).Method("DELETE").Path(subPath)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user