From 58d668182408a36f7204d80ee66feba32276f8f6 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Wed, 31 Jan 2018 19:10:04 +0100 Subject: [PATCH] Redirection: permanent move option. --- Gopkg.lock | 28 +- Gopkg.toml | 4 - autogen/gentemplates/gen.go | 8 + docs/configuration/backends/consulcatalog.md | 3 +- docs/configuration/backends/docker.md | 2 + docs/configuration/backends/ecs.md | 1 + docs/configuration/backends/file.md | 1 + docs/configuration/backends/kubernetes.md | 1 + docs/configuration/backends/marathon.md | 2 + docs/configuration/backends/mesos.md | 1 + docs/configuration/backends/rancher.md | 1 + middlewares/redirect/redirect.go | 144 +++ middlewares/redirect/redirect_test.go | 175 +++ middlewares/rewrite.go | 32 - .../consulcatalog/consul_catalog_config.go | 4 + .../consul_catalog_config_test.go | 24 + provider/docker/config_container.go | 4 + .../docker/config_container_docker_test.go | 32 + provider/docker/config_service.go | 4 + provider/docker/config_service_test.go | 2 + provider/ecs/config.go | 5 + provider/ecs/config_test.go | 32 + provider/kubernetes/annotations.go | 1 + provider/kubernetes/kubernetes.go | 4 + provider/kv/keynames.go | 1 + provider/kv/kv_config.go | 4 + provider/kv/kv_config_test.go | 28 + provider/label/names.go | 2 + provider/marathon/config.go | 5 + provider/marathon/config_test.go | 31 + provider/mesos/config.go | 4 + provider/mesos/config_test.go | 32 + provider/rancher/config.go | 4 + provider/rancher/config_test.go | 34 + server/server.go | 66 +- server/server_test.go | 129 +- templates/consul_catalog.tmpl | 1 + templates/docker.tmpl | 2 + templates/ecs.tmpl | 1 + templates/kv.tmpl | 1 + templates/marathon.tmpl | 1 + templates/mesos.tmpl | 1 + templates/rancher.tmpl | 1 + types/types.go | 1 + vendor/github.com/codegangsta/cli/LICENSE | 21 - vendor/github.com/codegangsta/cli/app.go | 497 -------- vendor/github.com/codegangsta/cli/category.go | 44 - vendor/github.com/codegangsta/cli/cli.go | 22 - vendor/github.com/codegangsta/cli/command.go | 304 ----- vendor/github.com/codegangsta/cli/context.go | 278 ----- vendor/github.com/codegangsta/cli/errors.go | 115 -- vendor/github.com/codegangsta/cli/flag.go | 799 ------------- .../codegangsta/cli/flag_generated.go | 627 ---------- vendor/github.com/codegangsta/cli/funcs.go | 28 - vendor/github.com/codegangsta/cli/help.go | 338 ------ vendor/github.com/vulcand/route/LICENSE | 202 ---- vendor/github.com/vulcand/route/iter.go | 88 -- vendor/github.com/vulcand/route/mapper.go | 186 --- vendor/github.com/vulcand/route/matcher.go | 155 --- vendor/github.com/vulcand/route/mux.go | 74 -- vendor/github.com/vulcand/route/parse.go | 47 - vendor/github.com/vulcand/route/router.go | 195 --- vendor/github.com/vulcand/route/trie.go | 476 -------- vendor/github.com/vulcand/route/utils.go | 33 - vendor/github.com/vulcand/vulcand/LICENSE | 201 ---- .../vulcand/conntracker/conntracker.go | 13 - vendor/github.com/vulcand/vulcand/main.go | 20 - .../plugin/cacheprovider/CacheProvider.go | 7 - .../cacheprovider/EtcdV2CacheProvider.go | 71 -- .../cacheprovider/EtcdV3CacheProvider.go | 76 -- .../plugin/cacheprovider/MemCacheProvider.go | 61 - .../plugin/cacheprovider/NoOpCacheProvider.go | 11 - .../vulcand/vulcand/plugin/middleware.go | 178 --- .../vulcand/vulcand/plugin/rewrite/rewrite.go | 205 ---- .../vulcand/plugin/rewrite/template.go | 44 - .../vulcand/vulcand/router/router.go | 26 - vendor/golang.org/x/crypto/acme/acme.go | 1058 ----------------- .../x/crypto/acme/autocert/autocert.go | 973 --------------- .../x/crypto/acme/autocert/cache.go | 130 -- .../x/crypto/acme/autocert/listener.go | 160 --- .../x/crypto/acme/autocert/renewal.go | 124 -- vendor/golang.org/x/crypto/acme/jws.go | 153 --- vendor/golang.org/x/crypto/acme/types.go | 329 ----- 83 files changed, 622 insertions(+), 8611 deletions(-) create mode 100644 middlewares/redirect/redirect.go create mode 100644 middlewares/redirect/redirect_test.go delete mode 100644 middlewares/rewrite.go delete mode 100644 vendor/github.com/codegangsta/cli/LICENSE delete mode 100644 vendor/github.com/codegangsta/cli/app.go delete mode 100644 vendor/github.com/codegangsta/cli/category.go delete mode 100644 vendor/github.com/codegangsta/cli/cli.go delete mode 100644 vendor/github.com/codegangsta/cli/command.go delete mode 100644 vendor/github.com/codegangsta/cli/context.go delete mode 100644 vendor/github.com/codegangsta/cli/errors.go delete mode 100644 vendor/github.com/codegangsta/cli/flag.go delete mode 100644 vendor/github.com/codegangsta/cli/flag_generated.go delete mode 100644 vendor/github.com/codegangsta/cli/funcs.go delete mode 100644 vendor/github.com/codegangsta/cli/help.go delete mode 100644 vendor/github.com/vulcand/route/LICENSE delete mode 100644 vendor/github.com/vulcand/route/iter.go delete mode 100644 vendor/github.com/vulcand/route/mapper.go delete mode 100644 vendor/github.com/vulcand/route/matcher.go delete mode 100644 vendor/github.com/vulcand/route/mux.go delete mode 100644 vendor/github.com/vulcand/route/parse.go delete mode 100644 vendor/github.com/vulcand/route/router.go delete mode 100644 vendor/github.com/vulcand/route/trie.go delete mode 100644 vendor/github.com/vulcand/route/utils.go delete mode 100644 vendor/github.com/vulcand/vulcand/LICENSE delete mode 100644 vendor/github.com/vulcand/vulcand/conntracker/conntracker.go delete mode 100644 vendor/github.com/vulcand/vulcand/main.go delete mode 100644 vendor/github.com/vulcand/vulcand/plugin/cacheprovider/CacheProvider.go delete mode 100644 vendor/github.com/vulcand/vulcand/plugin/cacheprovider/EtcdV2CacheProvider.go delete mode 100644 vendor/github.com/vulcand/vulcand/plugin/cacheprovider/EtcdV3CacheProvider.go delete mode 100644 vendor/github.com/vulcand/vulcand/plugin/cacheprovider/MemCacheProvider.go delete mode 100644 vendor/github.com/vulcand/vulcand/plugin/cacheprovider/NoOpCacheProvider.go delete mode 100644 vendor/github.com/vulcand/vulcand/plugin/middleware.go delete mode 100644 vendor/github.com/vulcand/vulcand/plugin/rewrite/rewrite.go delete mode 100644 vendor/github.com/vulcand/vulcand/plugin/rewrite/template.go delete mode 100644 vendor/github.com/vulcand/vulcand/router/router.go delete mode 100644 vendor/golang.org/x/crypto/acme/acme.go delete mode 100644 vendor/golang.org/x/crypto/acme/autocert/autocert.go delete mode 100644 vendor/golang.org/x/crypto/acme/autocert/cache.go delete mode 100644 vendor/golang.org/x/crypto/acme/autocert/listener.go delete mode 100644 vendor/golang.org/x/crypto/acme/autocert/renewal.go delete mode 100644 vendor/golang.org/x/crypto/acme/jws.go delete mode 100644 vendor/golang.org/x/crypto/acme/types.go diff --git a/Gopkg.lock b/Gopkg.lock index 42d1e9ae1..a2e52093d 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -222,12 +222,6 @@ packages = ["."] revision = "3a0bb77429bd3a61596f5e8a3172445844342120" -[[projects]] - name = "github.com/codegangsta/cli" - packages = ["."] - revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" - version = "v1.20.0" - [[projects]] branch = "master" name = "github.com/containerd/continuity" @@ -1158,24 +1152,6 @@ revision = "939c094524d124c55fa8afe0e077701db4a865e2" version = "v1.0.0" -[[projects]] - branch = "master" - name = "github.com/vulcand/route" - packages = ["."] - revision = "61904570391bdf22252f8e376b49a57593d9124c" - -[[projects]] - branch = "master" - name = "github.com/vulcand/vulcand" - packages = [ - "conntracker", - "plugin", - "plugin/cacheprovider", - "plugin/rewrite", - "router" - ] - revision = "3ab68471860269ba2afdd1a97e2d058087631975" - [[projects]] branch = "master" name = "github.com/xeipuuv/gojsonpointer" @@ -1228,8 +1204,6 @@ branch = "master" name = "golang.org/x/crypto" packages = [ - "acme", - "acme/autocert", "bcrypt", "blowfish", "ocsp", @@ -1518,6 +1492,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "b2cb3354504a6350e3022513b39206a3ece7c2f1a509bcef907c45422a8afb31" + inputs-digest = "daede4415dbd8614087d38fc4d8cba45d679bbb7185bfca805091ef7b295341f" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 5d57884cf..e0b4f0913 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -188,10 +188,6 @@ name = "k8s.io/client-go" version = "2.0.0" -[[constraint]] - branch = "master" - name = "github.com/vulcand/vulcand" - [[constraint]] branch = "master" name = "github.com/libkermit/docker" diff --git a/autogen/gentemplates/gen.go b/autogen/gentemplates/gen.go index fa2edcb28..60e0022ca 100644 --- a/autogen/gentemplates/gen.go +++ b/autogen/gentemplates/gen.go @@ -139,6 +139,7 @@ var _templatesConsul_catalogTmpl = []byte(`[backends] entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{if hasErrorPages $service.Attributes }} @@ -343,6 +344,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}} entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getServiceErrorPages $container $serviceName }} @@ -458,6 +460,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}} entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $container }} @@ -648,6 +651,7 @@ var _templatesEcsTmpl = []byte(`[backends] entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $instance }} @@ -1040,6 +1044,7 @@ var _templatesKvTmpl = []byte(`[backends] entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $frontend }} @@ -1249,6 +1254,7 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }} entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $app $serviceName }} @@ -1440,6 +1446,7 @@ var _templatesMesosTmpl = []byte(`[backends] entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $app }} @@ -1652,6 +1659,7 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }} entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $service }} diff --git a/docs/configuration/backends/consulcatalog.md b/docs/configuration/backends/consulcatalog.md index ac90741ea..864ee1b15 100644 --- a/docs/configuration/backends/consulcatalog.md +++ b/docs/configuration/backends/consulcatalog.md @@ -95,9 +95,10 @@ Additional settings can be defined using Consul Catalog tags. | `.frontend.rateLimit.rateSet..period=6` | See [custom error pages](/configuration/commons/#rate-limiting) section. | | `.frontend.rateLimit.rateSet..average=6` | See [custom error pages](/configuration/commons/#rate-limiting) section. | | `.frontend.rateLimit.rateSet..burst=6` | See [custom error pages](/configuration/commons/#rate-limiting) section. | -| `.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) . | +| `.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). | | `.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.replacement`. | | `.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `.frontend.redirect.permanent=true` | Return 301 instead of 302. | | `.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. | | `.frontend.whitelistSourceRange=RANGE` | List of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | diff --git a/docs/configuration/backends/docker.md b/docs/configuration/backends/docker.md index c57c432de..4158d1836 100644 --- a/docs/configuration/backends/docker.md +++ b/docs/configuration/backends/docker.md @@ -202,6 +202,7 @@ Labels can be used on containers to override default behaviour. | `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | | `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.replacement`. | | `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. | | `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | | `traefik.frontend.whitelistSourceRange=RANGE` | List of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | @@ -255,6 +256,7 @@ Services labels can be used for overriding default behaviour | `traefik..frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. | | `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. | | `traefik..frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. | +| `traefik..frontend.redirect.permanent=true` | Return 301 instead of 302. | | `traefik..frontend.rule` | Overrides `traefik.frontend.rule`. | | `traefik..frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. | diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index a5513c7dc..98fde1e12 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -162,6 +162,7 @@ Labels can be used on task containers to override default behaviour: | `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | | `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.replacement`. | | `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. | | `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{instance_name}.{domain}`. | | `traefik.frontend.whitelistSourceRange=RANGE` | List of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | diff --git a/docs/configuration/backends/file.md b/docs/configuration/backends/file.md index cff38aba5..361db9c39 100644 --- a/docs/configuration/backends/file.md +++ b/docs/configuration/backends/file.md @@ -119,6 +119,7 @@ Træfik can be configured with a file. entryPoint = "https" regex = "^http://localhost/(.*)" replacement = "http://mydomain/$1" + permanent = true [frontends.frontend2] # ... diff --git a/docs/configuration/backends/kubernetes.md b/docs/configuration/backends/kubernetes.md index 42cd00271..1c35a8936 100644 --- a/docs/configuration/backends/kubernetes.md +++ b/docs/configuration/backends/kubernetes.md @@ -110,6 +110,7 @@ The following general annotations are applicable on the Ingress object: | `traefik.ingress.kubernetes.io/priority: "3"` | Override the default frontend rule priority. | | `traefik.ingress.kubernetes.io/rate-limit: ` | (2) See [custom error pages](/configuration/commons/#rate-limiting) section. | | `traefik.ingress.kubernetes.io/redirect-entry-point: https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). | +| `traefik.ingress.kubernetes.io/redirect-permanent: true` | Return 301 instead of 302. | | `traefik.ingress.kubernetes.io/redirect-regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-replacement`. | | `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. | | `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. | diff --git a/docs/configuration/backends/marathon.md b/docs/configuration/backends/marathon.md index 6aac6fe17..d8e3a7acb 100644 --- a/docs/configuration/backends/marathon.md +++ b/docs/configuration/backends/marathon.md @@ -199,6 +199,7 @@ The following labels can be defined on Marathon applications. They adjust the be | `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | | `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.replacement`. | | `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. | | `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{sub_domain}.{domain}`. | | `traefik.frontend.whitelistSourceRange=RANGE` | List of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | @@ -253,6 +254,7 @@ For applications that expose multiple ports, specific labels can be used to extr | `traefik..frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. | | `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. | | `traefik..frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. | +| `traefik..frontend.redirect.permanent=true` | Return 301 instead of 302. | | `traefik..frontend.rule=EXP` | Overrides `traefik.frontend.rule`. Default: `{service_name}.{sub_domain}.{domain}` | | `traefik..frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. | diff --git a/docs/configuration/backends/mesos.md b/docs/configuration/backends/mesos.md index 56a8cc45b..ae14876c6 100644 --- a/docs/configuration/backends/mesos.md +++ b/docs/configuration/backends/mesos.md @@ -134,6 +134,7 @@ The following labels can be defined on Mesos tasks. They adjust the behaviour fo | `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | | `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.replacement`. | | `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. | | `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{discovery_name}.{domain}`. | | `traefik.frontend.whitelistSourceRange=RANGE` | List of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | diff --git a/docs/configuration/backends/rancher.md b/docs/configuration/backends/rancher.md index 79981b866..cba068930 100644 --- a/docs/configuration/backends/rancher.md +++ b/docs/configuration/backends/rancher.md @@ -158,6 +158,7 @@ Labels can be used on task containers to override default behaviour: | `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | | `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.replacement`. | | `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. | | `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{service_name}.{stack_name}.{domain}`. | | `traefik.frontend.whitelistSourceRange=RANGE` | List of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | diff --git a/middlewares/redirect/redirect.go b/middlewares/redirect/redirect.go new file mode 100644 index 000000000..655709948 --- /dev/null +++ b/middlewares/redirect/redirect.go @@ -0,0 +1,144 @@ +package redirect + +import ( + "bytes" + "fmt" + "io" + "net/http" + "net/url" + "regexp" + "strings" + "text/template" + + "github.com/containous/traefik/configuration" + "github.com/urfave/negroni" + "github.com/vulcand/oxy/utils" +) + +const ( + defaultRedirectRegex = `^(?:https?:\/\/)?([\w\._-]+)(?::\d+)?(.*)$` +) + +// NewEntryPointHandler create a new redirection handler base on entry point +func NewEntryPointHandler(dstEntryPoint *configuration.EntryPoint, permanent bool) (negroni.Handler, error) { + exp := regexp.MustCompile(`(:\d+)`) + match := exp.FindStringSubmatch(dstEntryPoint.Address) + if len(match) == 0 { + return nil, fmt.Errorf("bad Address format %q", dstEntryPoint.Address) + } + + protocol := "http" + if dstEntryPoint.TLS != nil { + protocol = "https" + } + + replacement := protocol + "://$1" + match[0] + "$2" + + return NewRegexHandler(defaultRedirectRegex, replacement, permanent) +} + +// NewRegexHandler create a new redirection handler base on regex +func NewRegexHandler(exp string, replacement string, permanent bool) (negroni.Handler, error) { + re, err := regexp.Compile(exp) + if err != nil { + return nil, err + } + + return &handler{ + regexp: re, + replacement: replacement, + permanent: permanent, + errHandler: utils.DefaultHandler, + }, nil +} + +type handler struct { + regexp *regexp.Regexp + replacement string + permanent bool + errHandler utils.ErrorHandler +} + +func (h *handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc) { + oldURL := rawURL(req) + + // only continue if the Regexp param matches the URL + if !h.regexp.MatchString(oldURL) { + next.ServeHTTP(rw, req) + return + } + + // apply a rewrite regexp to the URL + newURL := h.regexp.ReplaceAllString(oldURL, h.replacement) + + // replace any variables that may be in there + rewrittenURL := &bytes.Buffer{} + if err := applyString(newURL, rewrittenURL, req); err != nil { + h.errHandler.ServeHTTP(rw, req, err) + return + } + + // parse the rewritten URL and replace request URL with it + parsedURL, err := url.Parse(rewrittenURL.String()) + if err != nil { + h.errHandler.ServeHTTP(rw, req, err) + return + } + + if newURL != oldURL { + handler := &moveHandler{location: parsedURL, permanent: h.permanent} + handler.ServeHTTP(rw, req) + return + } + + req.URL = parsedURL + + // make sure the request URI corresponds the rewritten URL + req.RequestURI = req.URL.RequestURI() + next.ServeHTTP(rw, req) +} + +type moveHandler struct { + location *url.URL + permanent bool +} + +func (m *moveHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("Location", m.location.String()) + status := http.StatusFound + if m.permanent { + status = http.StatusMovedPermanently + } + rw.WriteHeader(status) + rw.Write([]byte(http.StatusText(status))) +} + +func rawURL(request *http.Request) string { + scheme := "http" + if request.TLS != nil || isXForwardedHTTPS(request) { + scheme = "https" + } + + return strings.Join([]string{scheme, "://", request.Host, request.RequestURI}, "") +} + +func isXForwardedHTTPS(request *http.Request) bool { + xForwardedProto := request.Header.Get("X-Forwarded-Proto") + + return len(xForwardedProto) > 0 && xForwardedProto == "https" +} + +func applyString(in string, out io.Writer, request *http.Request) error { + t, err := template.New("t").Parse(in) + if err != nil { + return err + } + + data := struct { + Request *http.Request + }{ + Request: request, + } + + return t.Execute(out, data) +} diff --git a/middlewares/redirect/redirect_test.go b/middlewares/redirect/redirect_test.go new file mode 100644 index 000000000..2c53f27dd --- /dev/null +++ b/middlewares/redirect/redirect_test.go @@ -0,0 +1,175 @@ +package redirect + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/containous/traefik/configuration" + "github.com/containous/traefik/testhelpers" + "github.com/containous/traefik/tls" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewEntryPointHandler(t *testing.T) { + testCases := []struct { + desc string + entryPoint *configuration.EntryPoint + permanent bool + url string + expectedURL string + expectedStatus int + errorExpected bool + }{ + { + desc: "HTTP to HTTPS", + entryPoint: &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}}, + url: "http://foo:80", + expectedURL: "https://foo:443", + expectedStatus: http.StatusFound, + }, + { + desc: "HTTPS to HTTP", + entryPoint: &configuration.EntryPoint{Address: ":80"}, + url: "https://foo:443", + expectedURL: "http://foo:80", + expectedStatus: http.StatusFound, + }, + { + desc: "HTTP to HTTP", + entryPoint: &configuration.EntryPoint{Address: ":88"}, + url: "http://foo:80", + expectedURL: "http://foo:88", + expectedStatus: http.StatusFound, + }, + { + desc: "HTTP to HTTPS permanent", + entryPoint: &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}}, + permanent: true, + url: "http://foo:80", + expectedURL: "https://foo:443", + expectedStatus: http.StatusMovedPermanently, + }, + { + desc: "HTTPS to HTTP permanent", + entryPoint: &configuration.EntryPoint{Address: ":80"}, + permanent: true, + url: "https://foo:443", + expectedURL: "http://foo:80", + expectedStatus: http.StatusMovedPermanently, + }, + { + desc: "invalid address", + entryPoint: &configuration.EntryPoint{Address: ":foo", TLS: &tls.TLS{}}, + url: "http://foo:80", + errorExpected: true, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + handler, err := NewEntryPointHandler(test.entryPoint, test.permanent) + + if test.errorExpected { + require.Error(t, err) + } else { + require.NoError(t, err) + + recorder := httptest.NewRecorder() + r := testhelpers.MustNewRequest(http.MethodGet, test.url, nil) + handler.ServeHTTP(recorder, r, nil) + + location, err := recorder.Result().Location() + require.NoError(t, err) + + assert.Equal(t, test.expectedURL, location.String()) + assert.Equal(t, test.expectedStatus, recorder.Code) + } + }) + } +} + +func TestNewRegexHandler(t *testing.T) { + testCases := []struct { + desc string + regex string + replacement string + permanent bool + url string + expectedURL string + expectedStatus int + errorExpected bool + }{ + { + desc: "simple redirection", + regex: `^(?:http?:\/\/)(foo)(\.com)(:\d+)(.*)$`, + replacement: "https://$1{{\"bar\"}}$2:443$4", + url: "http://foo.com:80", + expectedURL: "https://foobar.com:443", + expectedStatus: http.StatusFound, + }, + { + desc: "URL doesn't match regex", + regex: `^(?:http?:\/\/)(foo)(\.com)(:\d+)(.*)$`, + replacement: "https://$1{{\"bar\"}}$2:443$4", + url: "http://bar.com:80", + expectedStatus: http.StatusOK, + }, + { + desc: "invalid rewritten URL", + regex: `^(.*)$`, + replacement: "http://192.168.0.%31/", + url: "http://foo.com:80", + expectedStatus: http.StatusBadGateway, + }, + { + desc: "invalid regex", + regex: `^(.*`, + replacement: "$1", + url: "http://foo.com:80", + errorExpected: true, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + handler, err := NewRegexHandler(test.regex, test.replacement, test.permanent) + + if test.errorExpected { + require.Nil(t, handler) + fmt.Println(err == nil) + require.Error(t, err) + } else { + require.NotNil(t, handler) + require.NoError(t, err) + + recorder := httptest.NewRecorder() + r := testhelpers.MustNewRequest(http.MethodGet, test.url, nil) + next := func(rw http.ResponseWriter, req *http.Request) {} + handler.ServeHTTP(recorder, r, next) + + if test.expectedStatus == http.StatusMovedPermanently || test.expectedStatus == http.StatusFound { + assert.Equal(t, test.expectedStatus, recorder.Code) + + location, err := recorder.Result().Location() + require.NoError(t, err) + + assert.Equal(t, test.expectedURL, location.String()) + } else { + assert.Equal(t, test.expectedStatus, recorder.Code) + + location, err := recorder.Result().Location() + require.Error(t, err, "ghf %v", location) + } + } + }) + } +} diff --git a/middlewares/rewrite.go b/middlewares/rewrite.go deleted file mode 100644 index e130b5534..000000000 --- a/middlewares/rewrite.go +++ /dev/null @@ -1,32 +0,0 @@ -package middlewares - -import ( - "net/http" - - "github.com/containous/traefik/log" - "github.com/vulcand/vulcand/plugin/rewrite" -) - -// Rewrite is a middleware that allows redirections -type Rewrite struct { - rewriter *rewrite.Rewrite -} - -// NewRewrite creates a Rewrite middleware -func NewRewrite(regex, replacement string, redirect bool) (*Rewrite, error) { - rewriter, err := rewrite.NewRewrite(regex, replacement, false, redirect) - if err != nil { - return nil, err - } - return &Rewrite{rewriter: rewriter}, nil -} - -// -func (rewrite *Rewrite) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - handler, err := rewrite.rewriter.NewHandler(next) - if err != nil { - log.Error("Error in rewrite middleware ", err) - return - } - handler.ServeHTTP(rw, r) -} diff --git a/provider/consulcatalog/consul_catalog_config.go b/provider/consulcatalog/consul_catalog_config.go index 90760773b..779fa5837 100644 --- a/provider/consulcatalog/consul_catalog_config.go +++ b/provider/consulcatalog/consul_catalog_config.go @@ -312,9 +312,12 @@ func (p *Provider) getBuffering(tags []string) *types.Buffering { } func (p *Provider) getRedirect(tags []string) *types.Redirect { + permanent := p.getBoolAttribute(label.SuffixFrontendRedirectPermanent, tags, false) + if p.hasAttribute(label.SuffixFrontendRedirectEntryPoint, tags) { return &types.Redirect{ EntryPoint: p.getAttribute(label.SuffixFrontendRedirectEntryPoint, tags, ""), + Permanent: permanent, } } @@ -322,6 +325,7 @@ func (p *Provider) getRedirect(tags []string) *types.Redirect { return &types.Redirect{ Regex: p.getAttribute(label.SuffixFrontendRedirectRegex, tags, ""), Replacement: p.getAttribute(label.SuffixFrontendRedirectReplacement, tags, ""), + Permanent: permanent, } } diff --git a/provider/consulcatalog/consul_catalog_config_test.go b/provider/consulcatalog/consul_catalog_config_test.go index 9afb21db9..ac93d8418 100644 --- a/provider/consulcatalog/consul_catalog_config_test.go +++ b/provider/consulcatalog/consul_catalog_config_test.go @@ -1083,6 +1083,17 @@ func TestProviderGetRedirect(t *testing.T) { EntryPoint: "https", }, }, + { + desc: "should return a struct when entry point redirect tags (permanent)", + tags: []string{ + label.TraefikFrontendRedirectEntryPoint + "=https", + label.TraefikFrontendRedirectPermanent + "=true", + }, + expected: &types.Redirect{ + EntryPoint: "https", + Permanent: true, + }, + }, { desc: "should return a struct when regex redirect tags", tags: []string{ @@ -1094,6 +1105,19 @@ func TestProviderGetRedirect(t *testing.T) { Replacement: "$1", }, }, + { + desc: "should return a struct when regex redirect tags (permanent)", + tags: []string{ + label.TraefikFrontendRedirectRegex + "=(.*)", + label.TraefikFrontendRedirectReplacement + "=$1", + label.TraefikFrontendRedirectPermanent + "=true", + }, + expected: &types.Redirect{ + Regex: "(.*)", + Replacement: "$1", + Permanent: true, + }, + }, } for _, test := range testCases { diff --git a/provider/docker/config_container.go b/provider/docker/config_container.go index 772bc7b3d..22af65701 100644 --- a/provider/docker/config_container.go +++ b/provider/docker/config_container.go @@ -229,9 +229,12 @@ func getBuffering(container dockerData) *types.Buffering { } func getRedirect(container dockerData) *types.Redirect { + permanent := label.GetBoolValue(container.Labels, label.TraefikFrontendRedirectPermanent, false) + if label.Has(container.Labels, label.TraefikFrontendRedirectEntryPoint) { return &types.Redirect{ EntryPoint: label.GetStringValue(container.Labels, label.TraefikFrontendRedirectEntryPoint, ""), + Permanent: permanent, } } @@ -240,6 +243,7 @@ func getRedirect(container dockerData) *types.Redirect { return &types.Redirect{ Regex: label.GetStringValue(container.Labels, label.TraefikFrontendRedirectRegex, ""), Replacement: label.GetStringValue(container.Labels, label.TraefikFrontendRedirectReplacement, ""), + Permanent: permanent, } } diff --git a/provider/docker/config_container_docker_test.go b/provider/docker/config_container_docker_test.go index a25a74b8b..38af1d461 100644 --- a/provider/docker/config_container_docker_test.go +++ b/provider/docker/config_container_docker_test.go @@ -122,6 +122,7 @@ func TestDockerBuildConfiguration(t *testing.T) { label.TraefikFrontendRedirectEntryPoint: "https", label.TraefikFrontendRedirectRegex: "nope", label.TraefikFrontendRedirectReplacement: "nope", + label.TraefikFrontendRedirectPermanent: "true", label.TraefikFrontendRule: "Host:traefik.io", label.TraefikFrontendWhitelistSourceRange: "10.10.10.10", @@ -259,6 +260,7 @@ func TestDockerBuildConfiguration(t *testing.T) { EntryPoint: "https", Regex: "", Replacement: "", + Permanent: true, }, }, }, @@ -1221,6 +1223,20 @@ func TestDockerGetRedirect(t *testing.T) { EntryPoint: "https", }, }, + { + desc: "should return a struct when entry point redirect label (permanent)", + container: containerJSON( + name("test1"), + labels(map[string]string{ + label.TraefikFrontendRedirectEntryPoint: "https", + label.TraefikFrontendRedirectPermanent: "true", + }), + ), + expected: &types.Redirect{ + EntryPoint: "https", + Permanent: true, + }, + }, { desc: "should return a struct when regex redirect labels", container: containerJSON( @@ -1235,6 +1251,22 @@ func TestDockerGetRedirect(t *testing.T) { Replacement: "$1", }, }, + { + desc: "should return a struct when regex redirect tags (permanent)", + container: containerJSON( + name("test1"), + labels(map[string]string{ + label.TraefikFrontendRedirectRegex: "(.*)", + label.TraefikFrontendRedirectReplacement: "$1", + label.TraefikFrontendRedirectPermanent: "true", + }), + ), + expected: &types.Redirect{ + Regex: "(.*)", + Replacement: "$1", + Permanent: true, + }, + }, } for _, test := range testCases { diff --git a/provider/docker/config_service.go b/provider/docker/config_service.go index 72af79b01..5c79dafe4 100644 --- a/provider/docker/config_service.go +++ b/provider/docker/config_service.go @@ -100,9 +100,12 @@ func getServicePort(container dockerData, serviceName string) string { func getServiceRedirect(container dockerData, serviceName string) *types.Redirect { serviceLabels := getServiceLabels(container, serviceName) + permanent := getServiceBoolValue(container, serviceLabels, label.SuffixFrontendRedirectPermanent, false) + if hasStrictServiceLabel(serviceLabels, label.SuffixFrontendRedirectEntryPoint) { return &types.Redirect{ EntryPoint: getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint), + Permanent: permanent, } } @@ -111,6 +114,7 @@ func getServiceRedirect(container dockerData, serviceName string) *types.Redirec return &types.Redirect{ Regex: getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRedirectRegex, ""), Replacement: getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRedirectReplacement, ""), + Permanent: permanent, } } diff --git a/provider/docker/config_service_test.go b/provider/docker/config_service_test.go index 3a6307f18..7d549204e 100644 --- a/provider/docker/config_service_test.go +++ b/provider/docker/config_service_test.go @@ -86,6 +86,7 @@ func TestDockerServiceBuildConfiguration(t *testing.T) { label.Prefix + "service." + label.SuffixFrontendRedirectEntryPoint: "https", label.Prefix + "service." + label.SuffixFrontendRedirectRegex: "nope", label.Prefix + "service." + label.SuffixFrontendRedirectReplacement: "nope", + label.Prefix + "service." + label.SuffixFrontendRedirectPermanent: "true", label.Prefix + "service." + label.SuffixFrontendWhitelistSourceRange: "10.10.10.10", label.Prefix + "service." + label.SuffixFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", @@ -218,6 +219,7 @@ func TestDockerServiceBuildConfiguration(t *testing.T) { EntryPoint: "https", Regex: "", Replacement: "", + Permanent: true, }, Routes: map[string]types.Route{ diff --git a/provider/ecs/config.go b/provider/ecs/config.go index b21be0fa4..5f36f9430 100644 --- a/provider/ecs/config.go +++ b/provider/ecs/config.go @@ -212,16 +212,21 @@ func getServers(instances []ecsInstance) map[string]types.Server { } func getRedirect(instance ecsInstance) *types.Redirect { + permanent := getBoolValue(instance, label.TraefikFrontendRedirectPermanent, false) + if hasLabel(instance, label.TraefikFrontendRedirectEntryPoint) { return &types.Redirect{ EntryPoint: getStringValue(instance, label.TraefikFrontendRedirectEntryPoint, ""), + Permanent: permanent, } } + if hasLabel(instance, label.TraefikFrontendRedirectRegex) && hasLabel(instance, label.TraefikFrontendRedirectReplacement) { return &types.Redirect{ Regex: getStringValue(instance, label.TraefikFrontendRedirectRegex, ""), Replacement: getStringValue(instance, label.TraefikFrontendRedirectReplacement, ""), + Permanent: permanent, } } diff --git a/provider/ecs/config_test.go b/provider/ecs/config_test.go index 3bc2dc1e9..ecc344daf 100644 --- a/provider/ecs/config_test.go +++ b/provider/ecs/config_test.go @@ -150,6 +150,7 @@ func TestBuildConfiguration(t *testing.T) { label.TraefikFrontendRedirectEntryPoint: aws.String("https"), label.TraefikFrontendRedirectRegex: aws.String("nope"), label.TraefikFrontendRedirectReplacement: aws.String("nope"), + label.TraefikFrontendRedirectPermanent: aws.String("true"), label.TraefikFrontendRule: aws.String("Host:traefik.io"), label.TraefikFrontendWhitelistSourceRange: aws.String("10.10.10.10"), @@ -333,6 +334,7 @@ func TestBuildConfiguration(t *testing.T) { EntryPoint: "https", Regex: "", Replacement: "", + Permanent: true, }, }, }, @@ -1152,6 +1154,20 @@ func TestGetRedirect(t *testing.T) { EntryPoint: "https", }, }, + { + desc: "should return a struct when entry point redirect label (permanent)", + instance: ecsInstance{ + containerDefinition: &ecs.ContainerDefinition{ + DockerLabels: map[string]*string{ + label.TraefikFrontendRedirectEntryPoint: aws.String("https"), + label.TraefikFrontendRedirectPermanent: aws.String("true"), + }}, + }, + expected: &types.Redirect{ + EntryPoint: "https", + Permanent: true, + }, + }, { desc: "should return a struct when regex redirect labels", instance: ecsInstance{ @@ -1166,6 +1182,22 @@ func TestGetRedirect(t *testing.T) { Replacement: "$1", }, }, + { + desc: "should return a struct when regex redirect tags (permanent)", + instance: ecsInstance{ + containerDefinition: &ecs.ContainerDefinition{ + DockerLabels: map[string]*string{ + label.TraefikFrontendRedirectRegex: aws.String("(.*)"), + label.TraefikFrontendRedirectReplacement: aws.String("$1"), + label.TraefikFrontendRedirectPermanent: aws.String("true"), + }}, + }, + expected: &types.Redirect{ + Regex: "(.*)", + Replacement: "$1", + Permanent: true, + }, + }, } for _, test := range testCases { diff --git a/provider/kubernetes/annotations.go b/provider/kubernetes/annotations.go index 4a268b4f8..dddc18823 100644 --- a/provider/kubernetes/annotations.go +++ b/provider/kubernetes/annotations.go @@ -21,6 +21,7 @@ const ( annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name" annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type" annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point" + annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent" annotationKubernetesRedirectRegex = "ingress.kubernetes.io/redirect-regex" annotationKubernetesRedirectReplacement = "ingress.kubernetes.io/redirect-replacement" annotationKubernetesMaxConnAmount = "ingress.kubernetes.io/max-conn-amount" diff --git a/provider/kubernetes/kubernetes.go b/provider/kubernetes/kubernetes.go index b7a66ff57..078bc47fa 100644 --- a/provider/kubernetes/kubernetes.go +++ b/provider/kubernetes/kubernetes.go @@ -456,10 +456,13 @@ func shouldProcessIngress(ingressClass string) bool { } func getFrontendRedirect(i *v1beta1.Ingress) *types.Redirect { + permanent := getBoolValue(i.Annotations, annotationKubernetesRedirectPermanent, false) + redirectEntryPoint := getStringValue(i.Annotations, annotationKubernetesRedirectEntryPoint, "") if len(redirectEntryPoint) > 0 { return &types.Redirect{ EntryPoint: redirectEntryPoint, + Permanent: permanent, } } @@ -469,6 +472,7 @@ func getFrontendRedirect(i *v1beta1.Ingress) *types.Redirect { return &types.Redirect{ Regex: redirectRegex, Replacement: redirectReplacement, + Permanent: permanent, } } diff --git a/provider/kv/keynames.go b/provider/kv/keynames.go index d0c3f91b1..465b97601 100644 --- a/provider/kv/keynames.go +++ b/provider/kv/keynames.go @@ -33,6 +33,7 @@ const ( pathFrontendRedirectEntryPoint = "/redirect/entrypoint" pathFrontendRedirectRegex = "/redirect/regex" pathFrontendRedirectReplacement = "/redirect/replacement" + pathFrontendRedirectPermanent = "/redirect/permanent" pathFrontendErrorPages = "/errors/" pathFrontendErrorPagesBackend = "/backend" pathFrontendErrorPagesQuery = "/query" diff --git a/provider/kv/kv_config.go b/provider/kv/kv_config.go index 172f9f29a..5a8a6bc36 100644 --- a/provider/kv/kv_config.go +++ b/provider/kv/kv_config.go @@ -108,9 +108,12 @@ func (p *Provider) getStickinessCookieName(rootPath string) string { } func (p *Provider) getRedirect(rootPath string) *types.Redirect { + permanent := p.getBool(false, rootPath, pathFrontendRedirectPermanent) + if p.has(rootPath, pathFrontendRedirectEntryPoint) { return &types.Redirect{ EntryPoint: p.get("", rootPath, pathFrontendRedirectEntryPoint), + Permanent: permanent, } } @@ -118,6 +121,7 @@ func (p *Provider) getRedirect(rootPath string) *types.Redirect { return &types.Redirect{ Regex: p.get("", rootPath, pathFrontendRedirectRegex), Replacement: p.get("", rootPath, pathFrontendRedirectReplacement), + Permanent: permanent, } } diff --git a/provider/kv/kv_config_test.go b/provider/kv/kv_config_test.go index d95d96e93..a7ebd99a7 100644 --- a/provider/kv/kv_config_test.go +++ b/provider/kv/kv_config_test.go @@ -95,6 +95,7 @@ func TestProviderBuildConfiguration(t *testing.T) { withPair(pathFrontendRedirectEntryPoint, "https"), withPair(pathFrontendRedirectRegex, "nope"), withPair(pathFrontendRedirectReplacement, "nope"), + withPair(pathFrontendRedirectPermanent, "true"), withErrorPage("foo", "error", "/test1", "500-501, 503-599"), withErrorPage("bar", "error", "/test2", "400-405"), withRateLimit("client.ip", @@ -186,6 +187,7 @@ func TestProviderBuildConfiguration(t *testing.T) { BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, Redirect: &types.Redirect{ EntryPoint: "https", + Permanent: true, }, Errors: map[string]*types.ErrorPage{ "foo": { @@ -1044,6 +1046,18 @@ func TestProviderGetRedirect(t *testing.T) { EntryPoint: "https", }, }, + { + desc: "should use entry point when entry point key is valued in the store (permanent)", + rootPath: "traefik/frontends/foo", + kvPairs: filler("traefik", + frontend("foo", + withPair(pathFrontendRedirectEntryPoint, "https"), + withPair(pathFrontendRedirectPermanent, "true"))), + expected: &types.Redirect{ + EntryPoint: "https", + Permanent: true, + }, + }, { desc: "should use regex when regex keys are valued in the store", rootPath: "traefik/frontends/foo", @@ -1056,6 +1070,20 @@ func TestProviderGetRedirect(t *testing.T) { Replacement: "$1", }, }, + { + desc: "should use regex when regex keys are valued in the store (permanent)", + rootPath: "traefik/frontends/foo", + kvPairs: filler("traefik", + frontend("foo", + withPair(pathFrontendRedirectRegex, "(.*)"), + withPair(pathFrontendRedirectReplacement, "$1"), + withPair(pathFrontendRedirectPermanent, "true"))), + expected: &types.Redirect{ + Regex: "(.*)", + Replacement: "$1", + Permanent: true, + }, + }, { desc: "should only use entry point when entry point and regex base are valued in the store", rootPath: "traefik/frontends/foo", diff --git a/provider/label/names.go b/provider/label/names.go index c75a6dcdc..e7e8d82de 100644 --- a/provider/label/names.go +++ b/provider/label/names.go @@ -62,6 +62,7 @@ const ( SuffixFrontendRedirectEntryPoint = "frontend.redirect.entryPoint" SuffixFrontendRedirectRegex = "frontend.redirect.regex" SuffixFrontendRedirectReplacement = "frontend.redirect.replacement" + SuffixFrontendRedirectPermanent = "frontend.redirect.permanent" SuffixFrontendRule = "frontend.rule" SuffixFrontendRuleType = "frontend.rule.type" SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange" @@ -102,6 +103,7 @@ const ( TraefikFrontendRedirectEntryPoint = Prefix + SuffixFrontendRedirectEntryPoint TraefikFrontendRedirectRegex = Prefix + SuffixFrontendRedirectRegex TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement + TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent TraefikFrontendRule = Prefix + SuffixFrontendRule TraefikFrontendRuleType = Prefix + SuffixFrontendRuleType // k8s only TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange diff --git a/provider/marathon/config.go b/provider/marathon/config.go index 8deab6e20..9e66a94c9 100644 --- a/provider/marathon/config.go +++ b/provider/marathon/config.go @@ -501,16 +501,21 @@ func (p *Provider) getServers(application marathon.Application, serviceName stri func getRedirect(application marathon.Application, serviceName string) *types.Redirect { labels := getLabels(application, serviceName) + permanent := label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendRedirectPermanent), false) + if label.Has(labels, getLabelName(serviceName, label.SuffixFrontendRedirectEntryPoint)) { return &types.Redirect{ EntryPoint: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendRedirectEntryPoint), ""), + Permanent: permanent, } } + if label.Has(labels, getLabelName(serviceName, label.SuffixFrontendRedirectRegex)) && label.Has(labels, getLabelName(serviceName, label.SuffixFrontendRedirectReplacement)) { return &types.Redirect{ Regex: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendRedirectRegex), ""), Replacement: label.GetStringValue(labels, getLabelName(serviceName, label.SuffixFrontendRedirectReplacement), ""), + Permanent: permanent, } } diff --git a/provider/marathon/config_test.go b/provider/marathon/config_test.go index dfe6df5ea..d2fefa093 100644 --- a/provider/marathon/config_test.go +++ b/provider/marathon/config_test.go @@ -204,6 +204,7 @@ func TestBuildConfigurationNonAPIErrors(t *testing.T) { withLabel(label.TraefikFrontendRedirectEntryPoint, "https"), withLabel(label.TraefikFrontendRedirectRegex, "nope"), withLabel(label.TraefikFrontendRedirectReplacement, "nope"), + withLabel(label.TraefikFrontendRedirectPermanent, "true"), withLabel(label.TraefikFrontendRule, "Host:traefik.io"), withLabel(label.TraefikFrontendWhitelistSourceRange, "10.10.10.10"), @@ -342,6 +343,7 @@ func TestBuildConfigurationNonAPIErrors(t *testing.T) { }, Redirect: &types.Redirect{ EntryPoint: "https", + Permanent: true, }, }, }, @@ -524,6 +526,7 @@ func TestBuildConfigurationServicesNonAPIErrors(t *testing.T) { withServiceLabel(label.TraefikFrontendRedirectEntryPoint, "https", "containous"), withServiceLabel(label.TraefikFrontendRedirectRegex, "nope", "containous"), withServiceLabel(label.TraefikFrontendRedirectReplacement, "nope", "containous"), + withServiceLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"), withServiceLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"), withServiceLabel(label.TraefikFrontendWhitelistSourceRange, "10.10.10.10", "containous"), @@ -661,6 +664,7 @@ func TestBuildConfigurationServicesNonAPIErrors(t *testing.T) { }, Redirect: &types.Redirect{ EntryPoint: "https", + Permanent: true, }, }, }, @@ -1698,6 +1702,18 @@ func TestGetRedirect(t *testing.T) { EntryPoint: "https", }, }, + { + desc: "should return a struct when entry point redirect label (permanent)", + application: application( + appPorts(80), + withLabel(label.TraefikFrontendRedirectEntryPoint, "https"), + withLabel(label.TraefikFrontendRedirectPermanent, "true"), + ), + expected: &types.Redirect{ + EntryPoint: "https", + Permanent: true, + }, + }, { desc: "should return a struct when regex redirect labels", application: application( @@ -1748,6 +1764,21 @@ func TestGetRedirect(t *testing.T) { Replacement: "$1", }, }, + { + desc: "should return a struct when regex redirect labels on service (permanent)", + application: application( + appPorts(80), + withLabel(label.Prefix+"containous."+label.SuffixFrontendRedirectRegex, "(.*)"), + withLabel(label.Prefix+"containous."+label.SuffixFrontendRedirectReplacement, "$1"), + withLabel(label.Prefix+"containous."+label.SuffixFrontendRedirectPermanent, "true"), + ), + serviceName: "containous", + expected: &types.Redirect{ + Regex: "(.*)", + Replacement: "$1", + Permanent: true, + }, + }, } for _, test := range testCases { diff --git a/provider/mesos/config.go b/provider/mesos/config.go index d641b51bc..023a01146 100644 --- a/provider/mesos/config.go +++ b/provider/mesos/config.go @@ -338,9 +338,12 @@ func (p *Provider) getServers(tasks []state.Task) map[string]types.Server { } func getRedirect(task state.Task) *types.Redirect { + permanent := getBoolValue(task, label.TraefikFrontendRedirectPermanent, false) + if hasLabel(task, label.TraefikFrontendRedirectEntryPoint) { return &types.Redirect{ EntryPoint: getStringValue(task, label.TraefikFrontendRedirectEntryPoint, ""), + Permanent: permanent, } } @@ -349,6 +352,7 @@ func getRedirect(task state.Task) *types.Redirect { return &types.Redirect{ Regex: getStringValue(task, label.TraefikFrontendRedirectRegex, ""), Replacement: getStringValue(task, label.TraefikFrontendRedirectReplacement, ""), + Permanent: permanent, } } diff --git a/provider/mesos/config_test.go b/provider/mesos/config_test.go index 774f86370..f2c1447b2 100644 --- a/provider/mesos/config_test.go +++ b/provider/mesos/config_test.go @@ -146,6 +146,7 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikFrontendRedirectEntryPoint, "https"), withLabel(label.TraefikFrontendRedirectRegex, "nope"), withLabel(label.TraefikFrontendRedirectReplacement, "nope"), + withLabel(label.TraefikFrontendRedirectPermanent, "true"), withLabel(label.TraefikFrontendRule, "Host:traefik.io"), withLabel(label.TraefikFrontendWhitelistSourceRange, "10.10.10.10"), @@ -283,6 +284,7 @@ func TestBuildConfiguration(t *testing.T) { EntryPoint: "https", Regex: "", Replacement: "", + Permanent: true, }, }, }, @@ -991,6 +993,20 @@ func TestGetRedirect(t *testing.T) { EntryPoint: "https", }, }, + { + desc: "should return a struct when entry point redirect label (permanent)", + task: aTask("ID1", + withLabel(label.TraefikFrontendRedirectEntryPoint, "https"), + withLabel(label.TraefikFrontendRedirectPermanent, "true"), + withIP("10.10.10.10"), + withInfo("name1", withPorts(withPort("TCP", 80, "WEB"))), + withDefaultStatus(), + ), + expected: &types.Redirect{ + EntryPoint: "https", + Permanent: true, + }, + }, { desc: "should return a struct when regex redirect labels", task: aTask("ID1", @@ -1005,6 +1021,22 @@ func TestGetRedirect(t *testing.T) { Replacement: "$1", }, }, + { + desc: "should return a struct when regex redirect labels (permanent)", + task: aTask("ID1", + withLabel(label.TraefikFrontendRedirectRegex, "(.*)"), + withLabel(label.TraefikFrontendRedirectReplacement, "$1"), + withLabel(label.TraefikFrontendRedirectPermanent, "true"), + withIP("10.10.10.10"), + withInfo("name1", withPorts(withPort("TCP", 80, "WEB"))), + withDefaultStatus(), + ), + expected: &types.Redirect{ + Regex: "(.*)", + Replacement: "$1", + Permanent: true, + }, + }, } for _, test := range testCases { diff --git a/provider/rancher/config.go b/provider/rancher/config.go index 5bf01c57e..2c50ad378 100644 --- a/provider/rancher/config.go +++ b/provider/rancher/config.go @@ -273,9 +273,12 @@ func getServers(service rancherData) map[string]types.Server { } func getRedirect(service rancherData) *types.Redirect { + permanent := label.GetBoolValue(service.Labels, label.TraefikFrontendRedirectPermanent, false) + if label.Has(service.Labels, label.TraefikFrontendRedirectEntryPoint) { return &types.Redirect{ EntryPoint: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectEntryPoint, ""), + Permanent: permanent, } } @@ -284,6 +287,7 @@ func getRedirect(service rancherData) *types.Redirect { return &types.Redirect{ Regex: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectRegex, ""), Replacement: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectReplacement, ""), + Permanent: permanent, } } diff --git a/provider/rancher/config_test.go b/provider/rancher/config_test.go index f58a01ee3..6444b6baa 100644 --- a/provider/rancher/config_test.go +++ b/provider/rancher/config_test.go @@ -64,6 +64,7 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikFrontendRedirectEntryPoint: "https", label.TraefikFrontendRedirectRegex: "nope", label.TraefikFrontendRedirectReplacement: "nope", + label.TraefikFrontendRedirectPermanent: "true", label.TraefikFrontendRule: "Host:traefik.io", label.TraefikFrontendWhitelistSourceRange: "10.10.10.10", @@ -199,6 +200,7 @@ func TestProviderBuildConfiguration(t *testing.T) { EntryPoint: "https", Regex: "", Replacement: "", + Permanent: true, }, }, }, @@ -1041,6 +1043,21 @@ func TestGetRedirect(t *testing.T) { EntryPoint: "https", }, }, + { + desc: "should return a struct when entry point redirect label (permanent)", + service: rancherData{ + Labels: map[string]string{ + label.TraefikFrontendRedirectEntryPoint: "https", + label.TraefikFrontendRedirectPermanent: "true", + }, + Health: "healthy", + State: "active", + }, + expected: &types.Redirect{ + EntryPoint: "https", + Permanent: true, + }, + }, { desc: "should return a struct when regex redirect labels", service: rancherData{ @@ -1056,6 +1073,23 @@ func TestGetRedirect(t *testing.T) { Replacement: "$1", }, }, + { + desc: "should return a struct when regex redirect labels (permanent)", + service: rancherData{ + Labels: map[string]string{ + label.TraefikFrontendRedirectRegex: "(.*)", + label.TraefikFrontendRedirectReplacement: "$1", + label.TraefikFrontendRedirectPermanent: "true", + }, + Health: "healthy", + State: "active", + }, + expected: &types.Redirect{ + Regex: "(.*)", + Replacement: "$1", + Permanent: true, + }, + }, } for _, test := range testCases { diff --git a/server/server.go b/server/server.go index ef8b69505..ead264bf7 100644 --- a/server/server.go +++ b/server/server.go @@ -31,6 +31,7 @@ import ( "github.com/containous/traefik/middlewares" "github.com/containous/traefik/middlewares/accesslog" mauth "github.com/containous/traefik/middlewares/auth" + "github.com/containous/traefik/middlewares/redirect" "github.com/containous/traefik/middlewares/tracing" "github.com/containous/traefik/provider" "github.com/containous/traefik/safe" @@ -51,10 +52,6 @@ import ( "golang.org/x/net/http2" ) -const ( - defaultRedirectRegex = `^(?:https?:\/\/)?([\w\._-]+)(?::\d+)?(.*)$` -) - var ( httpServerLogger = stdlog.New(log.WriterLevel(logrus.DebugLevel), "", 0) ) @@ -1117,9 +1114,10 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura rewrite, err := s.buildRedirectHandler(entryPointName, frontend.Redirect) if err != nil { log.Errorf("Error creating Frontend Redirect: %v", err) + } else { + n.Use(s.wrapNegroniHandlerWithAccessLog(rewrite, fmt.Sprintf("frontend redirect for %s", frontendName))) + log.Debugf("Frontend %s redirect created", frontendName) } - n.Use(s.wrapNegroniHandlerWithAccessLog(rewrite, fmt.Sprintf("frontend redirect for %s", frontendName))) - log.Debugf("Frontend %s redirect created", frontendName) } if len(frontend.BasicAuth) > 0 { @@ -1282,57 +1280,25 @@ func (s *Server) wireFrontendBackend(serverRoute *serverRoute, handler http.Hand serverRoute.route.Handler(handler) } -func (s *Server) buildRedirectHandler(srcEntryPointName string, redirect *types.Redirect) (*middlewares.Rewrite, error) { +func (s *Server) buildRedirectHandler(srcEntryPointName string, opt *types.Redirect) (negroni.Handler, error) { // entry point redirect - if len(redirect.EntryPoint) > 0 { - return s.buildEntryPointRedirect(srcEntryPointName, redirect.EntryPoint) + if len(opt.EntryPoint) > 0 { + entryPoint := s.globalConfiguration.EntryPoints[opt.EntryPoint] + if entryPoint == nil { + return nil, fmt.Errorf("unknown target entrypoint %q", srcEntryPointName) + } + log.Debugf("Creating entry point redirect %s -> %s", srcEntryPointName, opt.EntryPoint) + return redirect.NewEntryPointHandler(entryPoint, opt.Permanent) } // regex redirect - rewrite, err := middlewares.NewRewrite(redirect.Regex, redirect.Replacement, true) + redirection, err := redirect.NewRegexHandler(opt.Regex, opt.Replacement, opt.Permanent) if err != nil { return nil, err } - log.Debugf("Creating entryPoint redirect %s -> %s -> %s", srcEntryPointName, redirect.Regex, redirect.Replacement) + log.Debugf("Creating regex redirect %s -> %s -> %s", srcEntryPointName, opt.Regex, opt.Replacement) - return rewrite, nil -} - -func (s *Server) buildEntryPointRedirect(srcEntryPointName string, redirectEntryPoint string) (*middlewares.Rewrite, error) { - regex, replacement, err := s.buildRedirect(redirectEntryPoint) - if err != nil { - return nil, err - } - - rewrite, err := middlewares.NewRewrite(regex, replacement, true) - if err != nil { - // Impossible case because error is always nil - return nil, err - } - log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", srcEntryPointName, redirectEntryPoint, regex, replacement) - - return rewrite, nil -} - -func (s *Server) buildRedirect(entryPointName string) (string, string, error) { - entryPoint := s.globalConfiguration.EntryPoints[entryPointName] - if entryPoint == nil { - return "", "", fmt.Errorf("unknown target entrypoint %q", entryPointName) - } - - exp := regexp.MustCompile(`(:\d+)`) - match := exp.FindStringSubmatch(entryPoint.Address) - if len(match) == 0 { - return "", "", fmt.Errorf("bad Address format %q", entryPoint.Address) - } - - var protocol = "http" - if s.globalConfiguration.EntryPoints[entryPointName].TLS != nil { - protocol = "https" - } - - replacement := protocol + "://$1" + match[0] + "$2" - return defaultRedirectRegex, replacement, nil + return redirection, nil } func (s *Server) buildDefaultHTTPRouter() *mux.Router { @@ -1440,7 +1406,7 @@ func registerMetricClients(metricsConfig *types.Metrics) metrics.Registry { return metrics.NewVoidRegistry() } - registries := []metrics.Registry{} + var registries []metrics.Registry if metricsConfig.Prometheus != nil { registries = append(registries, metrics.RegisterPrometheus(metricsConfig.Prometheus)) log.Debug("Configured Prometheus metrics") diff --git a/server/server_test.go b/server/server_test.go index e6e52604b..6449ad9a6 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -923,7 +923,7 @@ func TestServerResponseEmptyBackend(t *testing.T) { } } -func TestBuildEntryPointRedirect(t *testing.T) { +func TestBuildRedirectHandler(t *testing.T) { srv := Server{ globalConfiguration: configuration.GlobalConfiguration{ EntryPoints: configuration.EntryPoints{ @@ -1016,133 +1016,6 @@ func TestBuildEntryPointRedirect(t *testing.T) { } } -func TestServerBuildEntryPointRedirect(t *testing.T) { - srv := Server{ - globalConfiguration: configuration.GlobalConfiguration{ - EntryPoints: configuration.EntryPoints{ - "http": &configuration.EntryPoint{Address: ":80"}, - "https": &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}}, - }, - }, - } - - testCases := []struct { - desc string - srcEntryPointName string - redirectEntryPoint string - url string - expectedURL string - errorExpected bool - }{ - { - desc: "existing redirect entry point", - srcEntryPointName: "http", - redirectEntryPoint: "https", - url: "http://foo:80", - expectedURL: "https://foo:443", - }, - { - desc: "non-existing redirect entry point", - srcEntryPointName: "http", - redirectEntryPoint: "foo", - url: "http://foo:80", - errorExpected: true, - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - rewrite, err := srv.buildEntryPointRedirect(test.srcEntryPointName, test.redirectEntryPoint) - if test.errorExpected { - require.Error(t, err) - } else { - require.NoError(t, err) - - recorder := httptest.NewRecorder() - r := testhelpers.MustNewRequest(http.MethodGet, test.url, nil) - rewrite.ServeHTTP(recorder, r, nil) - - location, err := recorder.Result().Location() - require.NoError(t, err) - - assert.Equal(t, test.expectedURL, location.String()) - } - }) - } -} - -func TestServerBuildRedirect(t *testing.T) { - testCases := []struct { - desc string - globalConfiguration configuration.GlobalConfiguration - redirectEntryPointName string - expectedReplacement string - errorExpected bool - }{ - { - desc: "Redirect endpoint http to https with HTTPS protocol", - redirectEntryPointName: "https", - globalConfiguration: configuration.GlobalConfiguration{ - EntryPoints: configuration.EntryPoints{ - "http": &configuration.EntryPoint{Address: ":80"}, - "https": &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}}, - }, - }, - expectedReplacement: "https://$1:443$2", - }, - { - desc: "Redirect endpoint http to http02 with HTTP protocol", - redirectEntryPointName: "http02", - globalConfiguration: configuration.GlobalConfiguration{ - EntryPoints: configuration.EntryPoints{ - "http": &configuration.EntryPoint{Address: ":80"}, - "http02": &configuration.EntryPoint{Address: ":88"}, - }, - }, - expectedReplacement: "http://$1:88$2", - }, - { - desc: "Redirect endpoint to non-existent entry point", - redirectEntryPointName: "foobar", - globalConfiguration: configuration.GlobalConfiguration{ - EntryPoints: configuration.EntryPoints{ - "http": &configuration.EntryPoint{Address: ":80"}, - "http02": &configuration.EntryPoint{Address: ":88"}, - }, - }, - errorExpected: true, - }, - { - desc: "Redirect endpoint to an entry point with a malformed address", - redirectEntryPointName: "http02", - globalConfiguration: configuration.GlobalConfiguration{ - EntryPoints: configuration.EntryPoints{ - "http": &configuration.EntryPoint{Address: ":80"}, - "http02": &configuration.EntryPoint{Address: "88"}, - }, - }, - errorExpected: true, - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - srv := Server{globalConfiguration: test.globalConfiguration} - - _, replacement, err := srv.buildRedirect(test.redirectEntryPointName) - - require.Equal(t, test.errorExpected, err != nil, "Expected an error but don't have error, or Expected no error but have an error: %v", err) - assert.Equal(t, test.expectedReplacement, replacement, "build redirect does not return the right replacement pattern") - }) - } -} - func buildDynamicConfig(dynamicConfigBuilders ...func(*types.Configuration)) *types.Configuration { config := &types.Configuration{ Frontends: make(map[string]*types.Frontend), diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index f4c68a959..99431836d 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -83,6 +83,7 @@ entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{if hasErrorPages $service.Attributes }} diff --git a/templates/docker.tmpl b/templates/docker.tmpl index 44a013268..fce04d433 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -99,6 +99,7 @@ entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getServiceErrorPages $container $serviceName }} @@ -214,6 +215,7 @@ entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $container }} diff --git a/templates/ecs.tmpl b/templates/ecs.tmpl index 28349bb12..ba951e67b 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -84,6 +84,7 @@ entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $instance }} diff --git a/templates/kv.tmpl b/templates/kv.tmpl index 13a380f50..62089e140 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -83,6 +83,7 @@ entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $frontend }} diff --git a/templates/marathon.tmpl b/templates/marathon.tmpl index c7a45d124..10b52360d 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -90,6 +90,7 @@ entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $app $serviceName }} diff --git a/templates/mesos.tmpl b/templates/mesos.tmpl index a2e909c2f..ec9422e38 100644 --- a/templates/mesos.tmpl +++ b/templates/mesos.tmpl @@ -86,6 +86,7 @@ entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $app }} diff --git a/templates/rancher.tmpl b/templates/rancher.tmpl index 01bd12938..a732386b7 100644 --- a/templates/rancher.tmpl +++ b/templates/rancher.tmpl @@ -84,6 +84,7 @@ entryPoint = "{{ $redirect.EntryPoint }}" regex = "{{ $redirect.Regex }}" replacement = "{{ $redirect.Replacement }}" + permanent = {{ $redirect.Permanent }} {{end}} {{ $errorPages := getErrorPages $service }} diff --git a/types/types.go b/types/types.go index 9a22dd80e..7a7c90034 100644 --- a/types/types.go +++ b/types/types.go @@ -171,6 +171,7 @@ type Redirect struct { EntryPoint string `json:"entryPoint,omitempty"` Regex string `json:"regex,omitempty"` Replacement string `json:"replacement,omitempty"` + Permanent bool `json:"permanent,omitempty"` } // LoadBalancerMethod holds the method of load balancing to use. diff --git a/vendor/github.com/codegangsta/cli/LICENSE b/vendor/github.com/codegangsta/cli/LICENSE deleted file mode 100644 index 42a597e29..000000000 --- a/vendor/github.com/codegangsta/cli/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 Jeremy Saenz & Contributors - -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. diff --git a/vendor/github.com/codegangsta/cli/app.go b/vendor/github.com/codegangsta/cli/app.go deleted file mode 100644 index 51fc45d87..000000000 --- a/vendor/github.com/codegangsta/cli/app.go +++ /dev/null @@ -1,497 +0,0 @@ -package cli - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "sort" - "time" -) - -var ( - changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" - appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) - runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL) - - contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." - - errInvalidActionType = NewExitError("ERROR invalid Action type. "+ - fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ - fmt.Sprintf("See %s", appActionDeprecationURL), 2) -) - -// App is the main structure of a cli application. It is recommended that -// an app be created with the cli.NewApp() function -type App struct { - // The name of the program. Defaults to path.Base(os.Args[0]) - Name string - // Full name of command for help, defaults to Name - HelpName string - // Description of the program. - Usage string - // Text to override the USAGE section of help - UsageText string - // Description of the program argument format. - ArgsUsage string - // Version of the program - Version string - // Description of the program - Description string - // List of commands to execute - Commands []Command - // List of flags to parse - Flags []Flag - // Boolean to enable bash completion commands - EnableBashCompletion bool - // Boolean to hide built-in help command - HideHelp bool - // Boolean to hide built-in version flag and the VERSION section of help - HideVersion bool - // Populate on app startup, only gettable through method Categories() - categories CommandCategories - // An action to execute when the bash-completion flag is set - BashComplete BashCompleteFunc - // An action to execute before any subcommands are run, but after the context is ready - // If a non-nil error is returned, no subcommands are run - Before BeforeFunc - // An action to execute after any subcommands are run, but after the subcommand has finished - // It is run even if Action() panics - After AfterFunc - - // The action to execute when no subcommands are specified - // Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}` - // *Note*: support for the deprecated `Action` signature will be removed in a future version - Action interface{} - - // Execute this function if the proper command cannot be found - CommandNotFound CommandNotFoundFunc - // Execute this function if an usage error occurs - OnUsageError OnUsageErrorFunc - // Compilation date - Compiled time.Time - // List of all authors who contributed - Authors []Author - // Copyright of the binary if any - Copyright string - // Name of Author (Note: Use App.Authors, this is deprecated) - Author string - // Email of Author (Note: Use App.Authors, this is deprecated) - Email string - // Writer writer to write output to - Writer io.Writer - // ErrWriter writes error output - ErrWriter io.Writer - // Other custom info - Metadata map[string]interface{} - // Carries a function which returns app specific info. - ExtraInfo func() map[string]string - // CustomAppHelpTemplate the text template for app help topic. - // cli.go uses text/template to render templates. You can - // render custom help text by setting this variable. - CustomAppHelpTemplate string - - didSetup bool -} - -// Tries to find out when this binary was compiled. -// Returns the current time if it fails to find it. -func compileTime() time.Time { - info, err := os.Stat(os.Args[0]) - if err != nil { - return time.Now() - } - return info.ModTime() -} - -// NewApp creates a new cli Application with some reasonable defaults for Name, -// Usage, Version and Action. -func NewApp() *App { - return &App{ - Name: filepath.Base(os.Args[0]), - HelpName: filepath.Base(os.Args[0]), - Usage: "A new cli application", - UsageText: "", - Version: "0.0.0", - BashComplete: DefaultAppComplete, - Action: helpCommand.Action, - Compiled: compileTime(), - Writer: os.Stdout, - } -} - -// Setup runs initialization code to ensure all data structures are ready for -// `Run` or inspection prior to `Run`. It is internally called by `Run`, but -// will return early if setup has already happened. -func (a *App) Setup() { - if a.didSetup { - return - } - - a.didSetup = true - - if a.Author != "" || a.Email != "" { - a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) - } - - newCmds := []Command{} - for _, c := range a.Commands { - if c.HelpName == "" { - c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) - } - newCmds = append(newCmds, c) - } - a.Commands = newCmds - - if a.Command(helpCommand.Name) == nil && !a.HideHelp { - a.Commands = append(a.Commands, helpCommand) - if (HelpFlag != BoolFlag{}) { - a.appendFlag(HelpFlag) - } - } - - if !a.HideVersion { - a.appendFlag(VersionFlag) - } - - a.categories = CommandCategories{} - for _, command := range a.Commands { - a.categories = a.categories.AddCommand(command.Category, command) - } - sort.Sort(a.categories) - - if a.Metadata == nil { - a.Metadata = make(map[string]interface{}) - } - - if a.Writer == nil { - a.Writer = os.Stdout - } -} - -// Run is the entry point to the cli app. Parses the arguments slice and routes -// to the proper flag/args combination -func (a *App) Run(arguments []string) (err error) { - a.Setup() - - // handle the completion flag separately from the flagset since - // completion could be attempted after a flag, but before its value was put - // on the command line. this causes the flagset to interpret the completion - // flag name as the value of the flag before it which is undesirable - // note that we can only do this because the shell autocomplete function - // always appends the completion flag at the end of the command - shellComplete, arguments := checkShellCompleteFlag(a, arguments) - - // parse flags - set, err := flagSet(a.Name, a.Flags) - if err != nil { - return err - } - - set.SetOutput(ioutil.Discard) - err = set.Parse(arguments[1:]) - nerr := normalizeFlags(a.Flags, set) - context := NewContext(a, set, nil) - if nerr != nil { - fmt.Fprintln(a.Writer, nerr) - ShowAppHelp(context) - return nerr - } - context.shellComplete = shellComplete - - if checkCompletions(context) { - return nil - } - - if err != nil { - if a.OnUsageError != nil { - err := a.OnUsageError(context, err, false) - HandleExitCoder(err) - return err - } - fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - ShowAppHelp(context) - return err - } - - if !a.HideHelp && checkHelp(context) { - ShowAppHelp(context) - return nil - } - - if !a.HideVersion && checkVersion(context) { - ShowVersion(context) - return nil - } - - if a.After != nil { - defer func() { - if afterErr := a.After(context); afterErr != nil { - if err != nil { - err = NewMultiError(err, afterErr) - } else { - err = afterErr - } - } - }() - } - - if a.Before != nil { - beforeErr := a.Before(context) - if beforeErr != nil { - ShowAppHelp(context) - HandleExitCoder(beforeErr) - err = beforeErr - return err - } - } - - args := context.Args() - if args.Present() { - name := args.First() - c := a.Command(name) - if c != nil { - return c.Run(context) - } - } - - if a.Action == nil { - a.Action = helpCommand.Action - } - - // Run default Action - err = HandleAction(a.Action, context) - - HandleExitCoder(err) - return err -} - -// RunAndExitOnError calls .Run() and exits non-zero if an error was returned -// -// Deprecated: instead you should return an error that fulfills cli.ExitCoder -// to cli.App.Run. This will cause the application to exit with the given eror -// code in the cli.ExitCoder -func (a *App) RunAndExitOnError() { - if err := a.Run(os.Args); err != nil { - fmt.Fprintln(a.errWriter(), err) - OsExiter(1) - } -} - -// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to -// generate command-specific flags -func (a *App) RunAsSubcommand(ctx *Context) (err error) { - // append help to commands - if len(a.Commands) > 0 { - if a.Command(helpCommand.Name) == nil && !a.HideHelp { - a.Commands = append(a.Commands, helpCommand) - if (HelpFlag != BoolFlag{}) { - a.appendFlag(HelpFlag) - } - } - } - - newCmds := []Command{} - for _, c := range a.Commands { - if c.HelpName == "" { - c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) - } - newCmds = append(newCmds, c) - } - a.Commands = newCmds - - // parse flags - set, err := flagSet(a.Name, a.Flags) - if err != nil { - return err - } - - set.SetOutput(ioutil.Discard) - err = set.Parse(ctx.Args().Tail()) - nerr := normalizeFlags(a.Flags, set) - context := NewContext(a, set, ctx) - - if nerr != nil { - fmt.Fprintln(a.Writer, nerr) - fmt.Fprintln(a.Writer) - if len(a.Commands) > 0 { - ShowSubcommandHelp(context) - } else { - ShowCommandHelp(ctx, context.Args().First()) - } - return nerr - } - - if checkCompletions(context) { - return nil - } - - if err != nil { - if a.OnUsageError != nil { - err = a.OnUsageError(context, err, true) - HandleExitCoder(err) - return err - } - fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - ShowSubcommandHelp(context) - return err - } - - if len(a.Commands) > 0 { - if checkSubcommandHelp(context) { - return nil - } - } else { - if checkCommandHelp(ctx, context.Args().First()) { - return nil - } - } - - if a.After != nil { - defer func() { - afterErr := a.After(context) - if afterErr != nil { - HandleExitCoder(err) - if err != nil { - err = NewMultiError(err, afterErr) - } else { - err = afterErr - } - } - }() - } - - if a.Before != nil { - beforeErr := a.Before(context) - if beforeErr != nil { - HandleExitCoder(beforeErr) - err = beforeErr - return err - } - } - - args := context.Args() - if args.Present() { - name := args.First() - c := a.Command(name) - if c != nil { - return c.Run(context) - } - } - - // Run default Action - err = HandleAction(a.Action, context) - - HandleExitCoder(err) - return err -} - -// Command returns the named command on App. Returns nil if the command does not exist -func (a *App) Command(name string) *Command { - for _, c := range a.Commands { - if c.HasName(name) { - return &c - } - } - - return nil -} - -// Categories returns a slice containing all the categories with the commands they contain -func (a *App) Categories() CommandCategories { - return a.categories -} - -// VisibleCategories returns a slice of categories and commands that are -// Hidden=false -func (a *App) VisibleCategories() []*CommandCategory { - ret := []*CommandCategory{} - for _, category := range a.categories { - if visible := func() *CommandCategory { - for _, command := range category.Commands { - if !command.Hidden { - return category - } - } - return nil - }(); visible != nil { - ret = append(ret, visible) - } - } - return ret -} - -// VisibleCommands returns a slice of the Commands with Hidden=false -func (a *App) VisibleCommands() []Command { - ret := []Command{} - for _, command := range a.Commands { - if !command.Hidden { - ret = append(ret, command) - } - } - return ret -} - -// VisibleFlags returns a slice of the Flags with Hidden=false -func (a *App) VisibleFlags() []Flag { - return visibleFlags(a.Flags) -} - -func (a *App) hasFlag(flag Flag) bool { - for _, f := range a.Flags { - if flag == f { - return true - } - } - - return false -} - -func (a *App) errWriter() io.Writer { - - // When the app ErrWriter is nil use the package level one. - if a.ErrWriter == nil { - return ErrWriter - } - - return a.ErrWriter -} - -func (a *App) appendFlag(flag Flag) { - if !a.hasFlag(flag) { - a.Flags = append(a.Flags, flag) - } -} - -// Author represents someone who has contributed to a cli project. -type Author struct { - Name string // The Authors name - Email string // The Authors email -} - -// String makes Author comply to the Stringer interface, to allow an easy print in the templating process -func (a Author) String() string { - e := "" - if a.Email != "" { - e = " <" + a.Email + ">" - } - - return fmt.Sprintf("%v%v", a.Name, e) -} - -// HandleAction attempts to figure out which Action signature was used. If -// it's an ActionFunc or a func with the legacy signature for Action, the func -// is run! -func HandleAction(action interface{}, context *Context) (err error) { - if a, ok := action.(ActionFunc); ok { - return a(context) - } else if a, ok := action.(func(*Context) error); ok { - return a(context) - } else if a, ok := action.(func(*Context)); ok { // deprecated function signature - a(context) - return nil - } else { - return errInvalidActionType - } -} diff --git a/vendor/github.com/codegangsta/cli/category.go b/vendor/github.com/codegangsta/cli/category.go deleted file mode 100644 index 1a6055023..000000000 --- a/vendor/github.com/codegangsta/cli/category.go +++ /dev/null @@ -1,44 +0,0 @@ -package cli - -// CommandCategories is a slice of *CommandCategory. -type CommandCategories []*CommandCategory - -// CommandCategory is a category containing commands. -type CommandCategory struct { - Name string - Commands Commands -} - -func (c CommandCategories) Less(i, j int) bool { - return c[i].Name < c[j].Name -} - -func (c CommandCategories) Len() int { - return len(c) -} - -func (c CommandCategories) Swap(i, j int) { - c[i], c[j] = c[j], c[i] -} - -// AddCommand adds a command to a category. -func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { - for _, commandCategory := range c { - if commandCategory.Name == category { - commandCategory.Commands = append(commandCategory.Commands, command) - return c - } - } - return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) -} - -// VisibleCommands returns a slice of the Commands with Hidden=false -func (c *CommandCategory) VisibleCommands() []Command { - ret := []Command{} - for _, command := range c.Commands { - if !command.Hidden { - ret = append(ret, command) - } - } - return ret -} diff --git a/vendor/github.com/codegangsta/cli/cli.go b/vendor/github.com/codegangsta/cli/cli.go deleted file mode 100644 index 90c07eb8e..000000000 --- a/vendor/github.com/codegangsta/cli/cli.go +++ /dev/null @@ -1,22 +0,0 @@ -// Package cli provides a minimal framework for creating and organizing command line -// Go applications. cli is designed to be easy to understand and write, the most simple -// cli application can be written as follows: -// func main() { -// cli.NewApp().Run(os.Args) -// } -// -// Of course this application does not do much, so let's make this an actual application: -// func main() { -// app := cli.NewApp() -// app.Name = "greet" -// app.Usage = "say a greeting" -// app.Action = func(c *cli.Context) error { -// println("Greetings") -// return nil -// } -// -// app.Run(os.Args) -// } -package cli - -//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go diff --git a/vendor/github.com/codegangsta/cli/command.go b/vendor/github.com/codegangsta/cli/command.go deleted file mode 100644 index 23de2944b..000000000 --- a/vendor/github.com/codegangsta/cli/command.go +++ /dev/null @@ -1,304 +0,0 @@ -package cli - -import ( - "fmt" - "io/ioutil" - "sort" - "strings" -) - -// Command is a subcommand for a cli.App. -type Command struct { - // The name of the command - Name string - // short name of the command. Typically one character (deprecated, use `Aliases`) - ShortName string - // A list of aliases for the command - Aliases []string - // A short description of the usage of this command - Usage string - // Custom text to show on USAGE section of help - UsageText string - // A longer explanation of how the command works - Description string - // A short description of the arguments of this command - ArgsUsage string - // The category the command is part of - Category string - // The function to call when checking for bash command completions - BashComplete BashCompleteFunc - // An action to execute before any sub-subcommands are run, but after the context is ready - // If a non-nil error is returned, no sub-subcommands are run - Before BeforeFunc - // An action to execute after any subcommands are run, but after the subcommand has finished - // It is run even if Action() panics - After AfterFunc - // The function to call when this command is invoked - Action interface{} - // TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind - // of deprecation period has passed, maybe? - - // Execute this function if a usage error occurs. - OnUsageError OnUsageErrorFunc - // List of child commands - Subcommands Commands - // List of flags to parse - Flags []Flag - // Treat all flags as normal arguments if true - SkipFlagParsing bool - // Skip argument reordering which attempts to move flags before arguments, - // but only works if all flags appear after all arguments. This behavior was - // removed n version 2 since it only works under specific conditions so we - // backport here by exposing it as an option for compatibility. - SkipArgReorder bool - // Boolean to hide built-in help command - HideHelp bool - // Boolean to hide this command from help or completion - Hidden bool - - // Full name of command for help, defaults to full command name, including parent commands. - HelpName string - commandNamePath []string - - // CustomHelpTemplate the text template for the command help topic. - // cli.go uses text/template to render templates. You can - // render custom help text by setting this variable. - CustomHelpTemplate string -} - -type CommandsByName []Command - -func (c CommandsByName) Len() int { - return len(c) -} - -func (c CommandsByName) Less(i, j int) bool { - return c[i].Name < c[j].Name -} - -func (c CommandsByName) Swap(i, j int) { - c[i], c[j] = c[j], c[i] -} - -// FullName returns the full name of the command. -// For subcommands this ensures that parent commands are part of the command path -func (c Command) FullName() string { - if c.commandNamePath == nil { - return c.Name - } - return strings.Join(c.commandNamePath, " ") -} - -// Commands is a slice of Command -type Commands []Command - -// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags -func (c Command) Run(ctx *Context) (err error) { - if len(c.Subcommands) > 0 { - return c.startApp(ctx) - } - - if !c.HideHelp && (HelpFlag != BoolFlag{}) { - // append help to flags - c.Flags = append( - c.Flags, - HelpFlag, - ) - } - - set, err := flagSet(c.Name, c.Flags) - if err != nil { - return err - } - set.SetOutput(ioutil.Discard) - - if c.SkipFlagParsing { - err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) - } else if !c.SkipArgReorder { - firstFlagIndex := -1 - terminatorIndex := -1 - for index, arg := range ctx.Args() { - if arg == "--" { - terminatorIndex = index - break - } else if arg == "-" { - // Do nothing. A dash alone is not really a flag. - continue - } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { - firstFlagIndex = index - } - } - - if firstFlagIndex > -1 { - args := ctx.Args() - regularArgs := make([]string, len(args[1:firstFlagIndex])) - copy(regularArgs, args[1:firstFlagIndex]) - - var flagArgs []string - if terminatorIndex > -1 { - flagArgs = args[firstFlagIndex:terminatorIndex] - regularArgs = append(regularArgs, args[terminatorIndex:]...) - } else { - flagArgs = args[firstFlagIndex:] - } - - err = set.Parse(append(flagArgs, regularArgs...)) - } else { - err = set.Parse(ctx.Args().Tail()) - } - } else { - err = set.Parse(ctx.Args().Tail()) - } - - nerr := normalizeFlags(c.Flags, set) - if nerr != nil { - fmt.Fprintln(ctx.App.Writer, nerr) - fmt.Fprintln(ctx.App.Writer) - ShowCommandHelp(ctx, c.Name) - return nerr - } - - context := NewContext(ctx.App, set, ctx) - context.Command = c - if checkCommandCompletions(context, c.Name) { - return nil - } - - if err != nil { - if c.OnUsageError != nil { - err := c.OnUsageError(context, err, false) - HandleExitCoder(err) - return err - } - fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) - fmt.Fprintln(context.App.Writer) - ShowCommandHelp(context, c.Name) - return err - } - - if checkCommandHelp(context, c.Name) { - return nil - } - - if c.After != nil { - defer func() { - afterErr := c.After(context) - if afterErr != nil { - HandleExitCoder(err) - if err != nil { - err = NewMultiError(err, afterErr) - } else { - err = afterErr - } - } - }() - } - - if c.Before != nil { - err = c.Before(context) - if err != nil { - ShowCommandHelp(context, c.Name) - HandleExitCoder(err) - return err - } - } - - if c.Action == nil { - c.Action = helpSubcommand.Action - } - - err = HandleAction(c.Action, context) - - if err != nil { - HandleExitCoder(err) - } - return err -} - -// Names returns the names including short names and aliases. -func (c Command) Names() []string { - names := []string{c.Name} - - if c.ShortName != "" { - names = append(names, c.ShortName) - } - - return append(names, c.Aliases...) -} - -// HasName returns true if Command.Name or Command.ShortName matches given name -func (c Command) HasName(name string) bool { - for _, n := range c.Names() { - if n == name { - return true - } - } - return false -} - -func (c Command) startApp(ctx *Context) error { - app := NewApp() - app.Metadata = ctx.App.Metadata - // set the name and usage - app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) - if c.HelpName == "" { - app.HelpName = c.HelpName - } else { - app.HelpName = app.Name - } - - app.Usage = c.Usage - app.Description = c.Description - app.ArgsUsage = c.ArgsUsage - - // set CommandNotFound - app.CommandNotFound = ctx.App.CommandNotFound - app.CustomAppHelpTemplate = c.CustomHelpTemplate - - // set the flags and commands - app.Commands = c.Subcommands - app.Flags = c.Flags - app.HideHelp = c.HideHelp - - app.Version = ctx.App.Version - app.HideVersion = ctx.App.HideVersion - app.Compiled = ctx.App.Compiled - app.Author = ctx.App.Author - app.Email = ctx.App.Email - app.Writer = ctx.App.Writer - app.ErrWriter = ctx.App.ErrWriter - - app.categories = CommandCategories{} - for _, command := range c.Subcommands { - app.categories = app.categories.AddCommand(command.Category, command) - } - - sort.Sort(app.categories) - - // bash completion - app.EnableBashCompletion = ctx.App.EnableBashCompletion - if c.BashComplete != nil { - app.BashComplete = c.BashComplete - } - - // set the actions - app.Before = c.Before - app.After = c.After - if c.Action != nil { - app.Action = c.Action - } else { - app.Action = helpSubcommand.Action - } - app.OnUsageError = c.OnUsageError - - for index, cc := range app.Commands { - app.Commands[index].commandNamePath = []string{c.Name, cc.Name} - } - - return app.RunAsSubcommand(ctx) -} - -// VisibleFlags returns a slice of the Flags with Hidden=false -func (c Command) VisibleFlags() []Flag { - return visibleFlags(c.Flags) -} diff --git a/vendor/github.com/codegangsta/cli/context.go b/vendor/github.com/codegangsta/cli/context.go deleted file mode 100644 index db94191e2..000000000 --- a/vendor/github.com/codegangsta/cli/context.go +++ /dev/null @@ -1,278 +0,0 @@ -package cli - -import ( - "errors" - "flag" - "reflect" - "strings" - "syscall" -) - -// Context is a type that is passed through to -// each Handler action in a cli application. Context -// can be used to retrieve context-specific Args and -// parsed command-line options. -type Context struct { - App *App - Command Command - shellComplete bool - flagSet *flag.FlagSet - setFlags map[string]bool - parentContext *Context -} - -// NewContext creates a new context. For use in when invoking an App or Command action. -func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { - c := &Context{App: app, flagSet: set, parentContext: parentCtx} - - if parentCtx != nil { - c.shellComplete = parentCtx.shellComplete - } - - return c -} - -// NumFlags returns the number of flags set -func (c *Context) NumFlags() int { - return c.flagSet.NFlag() -} - -// Set sets a context flag to a value. -func (c *Context) Set(name, value string) error { - c.setFlags = nil - return c.flagSet.Set(name, value) -} - -// GlobalSet sets a context flag to a value on the global flagset -func (c *Context) GlobalSet(name, value string) error { - globalContext(c).setFlags = nil - return globalContext(c).flagSet.Set(name, value) -} - -// IsSet determines if the flag was actually set -func (c *Context) IsSet(name string) bool { - if c.setFlags == nil { - c.setFlags = make(map[string]bool) - - c.flagSet.Visit(func(f *flag.Flag) { - c.setFlags[f.Name] = true - }) - - c.flagSet.VisitAll(func(f *flag.Flag) { - if _, ok := c.setFlags[f.Name]; ok { - return - } - c.setFlags[f.Name] = false - }) - - // XXX hack to support IsSet for flags with EnvVar - // - // There isn't an easy way to do this with the current implementation since - // whether a flag was set via an environment variable is very difficult to - // determine here. Instead, we intend to introduce a backwards incompatible - // change in version 2 to add `IsSet` to the Flag interface to push the - // responsibility closer to where the information required to determine - // whether a flag is set by non-standard means such as environment - // variables is avaliable. - // - // See https://github.com/urfave/cli/issues/294 for additional discussion - flags := c.Command.Flags - if c.Command.Name == "" { // cannot == Command{} since it contains slice types - if c.App != nil { - flags = c.App.Flags - } - } - for _, f := range flags { - eachName(f.GetName(), func(name string) { - if isSet, ok := c.setFlags[name]; isSet || !ok { - return - } - - val := reflect.ValueOf(f) - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - - envVarValue := val.FieldByName("EnvVar") - if !envVarValue.IsValid() { - return - } - - eachName(envVarValue.String(), func(envVar string) { - envVar = strings.TrimSpace(envVar) - if _, ok := syscall.Getenv(envVar); ok { - c.setFlags[name] = true - return - } - }) - }) - } - } - - return c.setFlags[name] -} - -// GlobalIsSet determines if the global flag was actually set -func (c *Context) GlobalIsSet(name string) bool { - ctx := c - if ctx.parentContext != nil { - ctx = ctx.parentContext - } - - for ; ctx != nil; ctx = ctx.parentContext { - if ctx.IsSet(name) { - return true - } - } - return false -} - -// FlagNames returns a slice of flag names used in this context. -func (c *Context) FlagNames() (names []string) { - for _, flag := range c.Command.Flags { - name := strings.Split(flag.GetName(), ",")[0] - if name == "help" { - continue - } - names = append(names, name) - } - return -} - -// GlobalFlagNames returns a slice of global flag names used by the app. -func (c *Context) GlobalFlagNames() (names []string) { - for _, flag := range c.App.Flags { - name := strings.Split(flag.GetName(), ",")[0] - if name == "help" || name == "version" { - continue - } - names = append(names, name) - } - return -} - -// Parent returns the parent context, if any -func (c *Context) Parent() *Context { - return c.parentContext -} - -// value returns the value of the flag coressponding to `name` -func (c *Context) value(name string) interface{} { - return c.flagSet.Lookup(name).Value.(flag.Getter).Get() -} - -// Args contains apps console arguments -type Args []string - -// Args returns the command line arguments associated with the context. -func (c *Context) Args() Args { - args := Args(c.flagSet.Args()) - return args -} - -// NArg returns the number of the command line arguments. -func (c *Context) NArg() int { - return len(c.Args()) -} - -// Get returns the nth argument, or else a blank string -func (a Args) Get(n int) string { - if len(a) > n { - return a[n] - } - return "" -} - -// First returns the first argument, or else a blank string -func (a Args) First() string { - return a.Get(0) -} - -// Tail returns the rest of the arguments (not the first one) -// or else an empty string slice -func (a Args) Tail() []string { - if len(a) >= 2 { - return []string(a)[1:] - } - return []string{} -} - -// Present checks if there are any arguments present -func (a Args) Present() bool { - return len(a) != 0 -} - -// Swap swaps arguments at the given indexes -func (a Args) Swap(from, to int) error { - if from >= len(a) || to >= len(a) { - return errors.New("index out of range") - } - a[from], a[to] = a[to], a[from] - return nil -} - -func globalContext(ctx *Context) *Context { - if ctx == nil { - return nil - } - - for { - if ctx.parentContext == nil { - return ctx - } - ctx = ctx.parentContext - } -} - -func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { - if ctx.parentContext != nil { - ctx = ctx.parentContext - } - for ; ctx != nil; ctx = ctx.parentContext { - if f := ctx.flagSet.Lookup(name); f != nil { - return ctx.flagSet - } - } - return nil -} - -func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { - switch ff.Value.(type) { - case *StringSlice: - default: - set.Set(name, ff.Value.String()) - } -} - -func normalizeFlags(flags []Flag, set *flag.FlagSet) error { - visited := make(map[string]bool) - set.Visit(func(f *flag.Flag) { - visited[f.Name] = true - }) - for _, f := range flags { - parts := strings.Split(f.GetName(), ",") - if len(parts) == 1 { - continue - } - var ff *flag.Flag - for _, name := range parts { - name = strings.Trim(name, " ") - if visited[name] { - if ff != nil { - return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) - } - ff = set.Lookup(name) - } - } - if ff == nil { - continue - } - for _, name := range parts { - name = strings.Trim(name, " ") - if !visited[name] { - copyFlag(name, ff, set) - } - } - } - return nil -} diff --git a/vendor/github.com/codegangsta/cli/errors.go b/vendor/github.com/codegangsta/cli/errors.go deleted file mode 100644 index 562b2953c..000000000 --- a/vendor/github.com/codegangsta/cli/errors.go +++ /dev/null @@ -1,115 +0,0 @@ -package cli - -import ( - "fmt" - "io" - "os" - "strings" -) - -// OsExiter is the function used when the app exits. If not set defaults to os.Exit. -var OsExiter = os.Exit - -// ErrWriter is used to write errors to the user. This can be anything -// implementing the io.Writer interface and defaults to os.Stderr. -var ErrWriter io.Writer = os.Stderr - -// MultiError is an error that wraps multiple errors. -type MultiError struct { - Errors []error -} - -// NewMultiError creates a new MultiError. Pass in one or more errors. -func NewMultiError(err ...error) MultiError { - return MultiError{Errors: err} -} - -// Error implements the error interface. -func (m MultiError) Error() string { - errs := make([]string, len(m.Errors)) - for i, err := range m.Errors { - errs[i] = err.Error() - } - - return strings.Join(errs, "\n") -} - -type ErrorFormatter interface { - Format(s fmt.State, verb rune) -} - -// ExitCoder is the interface checked by `App` and `Command` for a custom exit -// code -type ExitCoder interface { - error - ExitCode() int -} - -// ExitError fulfills both the builtin `error` interface and `ExitCoder` -type ExitError struct { - exitCode int - message interface{} -} - -// NewExitError makes a new *ExitError -func NewExitError(message interface{}, exitCode int) *ExitError { - return &ExitError{ - exitCode: exitCode, - message: message, - } -} - -// Error returns the string message, fulfilling the interface required by -// `error` -func (ee *ExitError) Error() string { - return fmt.Sprintf("%v", ee.message) -} - -// ExitCode returns the exit code, fulfilling the interface required by -// `ExitCoder` -func (ee *ExitError) ExitCode() int { - return ee.exitCode -} - -// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if -// so prints the error to stderr (if it is non-empty) and calls OsExiter with the -// given exit code. If the given error is a MultiError, then this func is -// called on all members of the Errors slice and calls OsExiter with the last exit code. -func HandleExitCoder(err error) { - if err == nil { - return - } - - if exitErr, ok := err.(ExitCoder); ok { - if err.Error() != "" { - if _, ok := exitErr.(ErrorFormatter); ok { - fmt.Fprintf(ErrWriter, "%+v\n", err) - } else { - fmt.Fprintln(ErrWriter, err) - } - } - OsExiter(exitErr.ExitCode()) - return - } - - if multiErr, ok := err.(MultiError); ok { - code := handleMultiError(multiErr) - OsExiter(code) - return - } -} - -func handleMultiError(multiErr MultiError) int { - code := 1 - for _, merr := range multiErr.Errors { - if multiErr2, ok := merr.(MultiError); ok { - code = handleMultiError(multiErr2) - } else { - fmt.Fprintln(ErrWriter, merr) - if exitErr, ok := merr.(ExitCoder); ok { - code = exitErr.ExitCode() - } - } - } - return code -} diff --git a/vendor/github.com/codegangsta/cli/flag.go b/vendor/github.com/codegangsta/cli/flag.go deleted file mode 100644 index 877ff3523..000000000 --- a/vendor/github.com/codegangsta/cli/flag.go +++ /dev/null @@ -1,799 +0,0 @@ -package cli - -import ( - "flag" - "fmt" - "reflect" - "runtime" - "strconv" - "strings" - "syscall" - "time" -) - -const defaultPlaceholder = "value" - -// BashCompletionFlag enables bash-completion for all commands and subcommands -var BashCompletionFlag Flag = BoolFlag{ - Name: "generate-bash-completion", - Hidden: true, -} - -// VersionFlag prints the version for the application -var VersionFlag Flag = BoolFlag{ - Name: "version, v", - Usage: "print the version", -} - -// HelpFlag prints the help for all commands and subcommands -// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand -// unless HideHelp is set to true) -var HelpFlag Flag = BoolFlag{ - Name: "help, h", - Usage: "show help", -} - -// FlagStringer converts a flag definition to a string. This is used by help -// to display a flag. -var FlagStringer FlagStringFunc = stringifyFlag - -// FlagsByName is a slice of Flag. -type FlagsByName []Flag - -func (f FlagsByName) Len() int { - return len(f) -} - -func (f FlagsByName) Less(i, j int) bool { - return f[i].GetName() < f[j].GetName() -} - -func (f FlagsByName) Swap(i, j int) { - f[i], f[j] = f[j], f[i] -} - -// Flag is a common interface related to parsing flags in cli. -// For more advanced flag parsing techniques, it is recommended that -// this interface be implemented. -type Flag interface { - fmt.Stringer - // Apply Flag settings to the given flag set - Apply(*flag.FlagSet) - GetName() string -} - -// errorableFlag is an interface that allows us to return errors during apply -// it allows flags defined in this library to return errors in a fashion backwards compatible -// TODO remove in v2 and modify the existing Flag interface to return errors -type errorableFlag interface { - Flag - - ApplyWithError(*flag.FlagSet) error -} - -func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { - set := flag.NewFlagSet(name, flag.ContinueOnError) - - for _, f := range flags { - //TODO remove in v2 when errorableFlag is removed - if ef, ok := f.(errorableFlag); ok { - if err := ef.ApplyWithError(set); err != nil { - return nil, err - } - } else { - f.Apply(set) - } - } - return set, nil -} - -func eachName(longName string, fn func(string)) { - parts := strings.Split(longName, ",") - for _, name := range parts { - name = strings.Trim(name, " ") - fn(name) - } -} - -// Generic is a generic parseable type identified by a specific flag -type Generic interface { - Set(value string) error - String() string -} - -// Apply takes the flagset and calls Set on the generic flag with the value -// provided by the user for parsing by the flag -// Ignores parsing errors -func (f GenericFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError takes the flagset and calls Set on the generic flag with the value -// provided by the user for parsing by the flag -func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { - val := f.Value - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - if err := val.Set(envVal); err != nil { - return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err) - } - break - } - } - } - - eachName(f.Name, func(name string) { - set.Var(f.Value, name, f.Usage) - }) - - return nil -} - -// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter -type StringSlice []string - -// Set appends the string value to the list of values -func (f *StringSlice) Set(value string) error { - *f = append(*f, value) - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *StringSlice) String() string { - return fmt.Sprintf("%s", *f) -} - -// Value returns the slice of strings set by this flag -func (f *StringSlice) Value() []string { - return *f -} - -// Get returns the slice of strings set by this flag -func (f *StringSlice) Get() interface{} { - return *f -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f StringSliceFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - newVal := &StringSlice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - if err := newVal.Set(s); err != nil { - return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) - } - } - f.Value = newVal - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &StringSlice{} - } - set.Var(f.Value, name, f.Usage) - }) - - return nil -} - -// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter -type IntSlice []int - -// Set parses the value into an integer and appends it to the list of values -func (f *IntSlice) Set(value string) error { - tmp, err := strconv.Atoi(value) - if err != nil { - return err - } - *f = append(*f, tmp) - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *IntSlice) String() string { - return fmt.Sprintf("%#v", *f) -} - -// Value returns the slice of ints set by this flag -func (f *IntSlice) Value() []int { - return *f -} - -// Get returns the slice of ints set by this flag -func (f *IntSlice) Get() interface{} { - return *f -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f IntSliceFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - newVal := &IntSlice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - if err := newVal.Set(s); err != nil { - return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) - } - } - f.Value = newVal - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &IntSlice{} - } - set.Var(f.Value, name, f.Usage) - }) - - return nil -} - -// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter -type Int64Slice []int64 - -// Set parses the value into an integer and appends it to the list of values -func (f *Int64Slice) Set(value string) error { - tmp, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - *f = append(*f, tmp) - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *Int64Slice) String() string { - return fmt.Sprintf("%#v", *f) -} - -// Value returns the slice of ints set by this flag -func (f *Int64Slice) Value() []int64 { - return *f -} - -// Get returns the slice of ints set by this flag -func (f *Int64Slice) Get() interface{} { - return *f -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f Int64SliceFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - newVal := &Int64Slice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - if err := newVal.Set(s); err != nil { - return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) - } - } - f.Value = newVal - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &Int64Slice{} - } - set.Var(f.Value, name, f.Usage) - }) - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f BoolFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { - val := false - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - if envVal == "" { - val = false - break - } - - envValBool, err := strconv.ParseBool(envVal) - if err != nil { - return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) - } - - val = envValBool - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.BoolVar(f.Destination, name, val, f.Usage) - return - } - set.Bool(name, val, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f BoolTFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { - val := true - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - if envVal == "" { - val = false - break - } - - envValBool, err := strconv.ParseBool(envVal) - if err != nil { - return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) - } - - val = envValBool - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.BoolVar(f.Destination, name, val, f.Usage) - return - } - set.Bool(name, val, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f StringFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - f.Value = envVal - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.StringVar(f.Destination, name, f.Value, f.Usage) - return - } - set.String(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f IntFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - envValInt, err := strconv.ParseInt(envVal, 0, 64) - if err != nil { - return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) - } - f.Value = int(envValInt) - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.IntVar(f.Destination, name, f.Value, f.Usage) - return - } - set.Int(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f Int64Flag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - envValInt, err := strconv.ParseInt(envVal, 0, 64) - if err != nil { - return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) - } - - f.Value = envValInt - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.Int64Var(f.Destination, name, f.Value, f.Usage) - return - } - set.Int64(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f UintFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - envValInt, err := strconv.ParseUint(envVal, 0, 64) - if err != nil { - return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) - } - - f.Value = uint(envValInt) - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.UintVar(f.Destination, name, f.Value, f.Usage) - return - } - set.Uint(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f Uint64Flag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - envValInt, err := strconv.ParseUint(envVal, 0, 64) - if err != nil { - return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) - } - - f.Value = uint64(envValInt) - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.Uint64Var(f.Destination, name, f.Value, f.Usage) - return - } - set.Uint64(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f DurationFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - envValDuration, err := time.ParseDuration(envVal) - if err != nil { - return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) - } - - f.Value = envValDuration - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.DurationVar(f.Destination, name, f.Value, f.Usage) - return - } - set.Duration(name, f.Value, f.Usage) - }) - - return nil -} - -// Apply populates the flag given the flag set and environment -// Ignores errors -func (f Float64Flag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) -} - -// ApplyWithError populates the flag given the flag set and environment -func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) - if envVal, ok := syscall.Getenv(envVar); ok { - envValFloat, err := strconv.ParseFloat(envVal, 10) - if err != nil { - return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) - } - - f.Value = float64(envValFloat) - break - } - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.Float64Var(f.Destination, name, f.Value, f.Usage) - return - } - set.Float64(name, f.Value, f.Usage) - }) - - return nil -} - -func visibleFlags(fl []Flag) []Flag { - visible := []Flag{} - for _, flag := range fl { - field := flagValue(flag).FieldByName("Hidden") - if !field.IsValid() || !field.Bool() { - visible = append(visible, flag) - } - } - return visible -} - -func prefixFor(name string) (prefix string) { - if len(name) == 1 { - prefix = "-" - } else { - prefix = "--" - } - - return -} - -// Returns the placeholder, if any, and the unquoted usage string. -func unquoteUsage(usage string) (string, string) { - for i := 0; i < len(usage); i++ { - if usage[i] == '`' { - for j := i + 1; j < len(usage); j++ { - if usage[j] == '`' { - name := usage[i+1 : j] - usage = usage[:i] + name + usage[j+1:] - return name, usage - } - } - break - } - } - return "", usage -} - -func prefixedNames(fullName, placeholder string) string { - var prefixed string - parts := strings.Split(fullName, ",") - for i, name := range parts { - name = strings.Trim(name, " ") - prefixed += prefixFor(name) + name - if placeholder != "" { - prefixed += " " + placeholder - } - if i < len(parts)-1 { - prefixed += ", " - } - } - return prefixed -} - -func withEnvHint(envVar, str string) string { - envText := "" - if envVar != "" { - prefix := "$" - suffix := "" - sep := ", $" - if runtime.GOOS == "windows" { - prefix = "%" - suffix = "%" - sep = "%, %" - } - envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix) - } - return str + envText -} - -func flagValue(f Flag) reflect.Value { - fv := reflect.ValueOf(f) - for fv.Kind() == reflect.Ptr { - fv = reflect.Indirect(fv) - } - return fv -} - -func stringifyFlag(f Flag) string { - fv := flagValue(f) - - switch f.(type) { - case IntSliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), - stringifyIntSliceFlag(f.(IntSliceFlag))) - case Int64SliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), - stringifyInt64SliceFlag(f.(Int64SliceFlag))) - case StringSliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), - stringifyStringSliceFlag(f.(StringSliceFlag))) - } - - placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) - - needsPlaceholder := false - defaultValueString := "" - - if val := fv.FieldByName("Value"); val.IsValid() { - needsPlaceholder = true - defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface()) - - if val.Kind() == reflect.String && val.String() != "" { - defaultValueString = fmt.Sprintf(" (default: %q)", val.String()) - } - } - - if defaultValueString == " (default: )" { - defaultValueString = "" - } - - if needsPlaceholder && placeholder == "" { - placeholder = defaultPlaceholder - } - - usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString)) - - return withEnvHint(fv.FieldByName("EnvVar").String(), - fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault)) -} - -func stringifyIntSliceFlag(f IntSliceFlag) string { - defaultVals := []string{} - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) - } - } - - return stringifySliceFlag(f.Usage, f.Name, defaultVals) -} - -func stringifyInt64SliceFlag(f Int64SliceFlag) string { - defaultVals := []string{} - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) - } - } - - return stringifySliceFlag(f.Usage, f.Name, defaultVals) -} - -func stringifyStringSliceFlag(f StringSliceFlag) string { - defaultVals := []string{} - if f.Value != nil && len(f.Value.Value()) > 0 { - for _, s := range f.Value.Value() { - if len(s) > 0 { - defaultVals = append(defaultVals, fmt.Sprintf("%q", s)) - } - } - } - - return stringifySliceFlag(f.Usage, f.Name, defaultVals) -} - -func stringifySliceFlag(usage, name string, defaultVals []string) string { - placeholder, usage := unquoteUsage(usage) - if placeholder == "" { - placeholder = defaultPlaceholder - } - - defaultVal := "" - if len(defaultVals) > 0 { - defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", ")) - } - - usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) - return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault) -} diff --git a/vendor/github.com/codegangsta/cli/flag_generated.go b/vendor/github.com/codegangsta/cli/flag_generated.go deleted file mode 100644 index 491b61956..000000000 --- a/vendor/github.com/codegangsta/cli/flag_generated.go +++ /dev/null @@ -1,627 +0,0 @@ -package cli - -import ( - "flag" - "strconv" - "time" -) - -// WARNING: This file is generated! - -// BoolFlag is a flag with type bool -type BoolFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Destination *bool -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f BoolFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f BoolFlag) GetName() string { - return f.Name -} - -// Bool looks up the value of a local BoolFlag, returns -// false if not found -func (c *Context) Bool(name string) bool { - return lookupBool(name, c.flagSet) -} - -// GlobalBool looks up the value of a global BoolFlag, returns -// false if not found -func (c *Context) GlobalBool(name string) bool { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupBool(name, fs) - } - return false -} - -func lookupBool(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return false - } - return parsed - } - return false -} - -// BoolTFlag is a flag with type bool that is true by default -type BoolTFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Destination *bool -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f BoolTFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f BoolTFlag) GetName() string { - return f.Name -} - -// BoolT looks up the value of a local BoolTFlag, returns -// false if not found -func (c *Context) BoolT(name string) bool { - return lookupBoolT(name, c.flagSet) -} - -// GlobalBoolT looks up the value of a global BoolTFlag, returns -// false if not found -func (c *Context) GlobalBoolT(name string) bool { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupBoolT(name, fs) - } - return false -} - -func lookupBoolT(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return false - } - return parsed - } - return false -} - -// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) -type DurationFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value time.Duration - Destination *time.Duration -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f DurationFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f DurationFlag) GetName() string { - return f.Name -} - -// Duration looks up the value of a local DurationFlag, returns -// 0 if not found -func (c *Context) Duration(name string) time.Duration { - return lookupDuration(name, c.flagSet) -} - -// GlobalDuration looks up the value of a global DurationFlag, returns -// 0 if not found -func (c *Context) GlobalDuration(name string) time.Duration { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupDuration(name, fs) - } - return 0 -} - -func lookupDuration(name string, set *flag.FlagSet) time.Duration { - f := set.Lookup(name) - if f != nil { - parsed, err := time.ParseDuration(f.Value.String()) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// Float64Flag is a flag with type float64 -type Float64Flag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value float64 - Destination *float64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Float64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Float64Flag) GetName() string { - return f.Name -} - -// Float64 looks up the value of a local Float64Flag, returns -// 0 if not found -func (c *Context) Float64(name string) float64 { - return lookupFloat64(name, c.flagSet) -} - -// GlobalFloat64 looks up the value of a global Float64Flag, returns -// 0 if not found -func (c *Context) GlobalFloat64(name string) float64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupFloat64(name, fs) - } - return 0 -} - -func lookupFloat64(name string, set *flag.FlagSet) float64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseFloat(f.Value.String(), 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// GenericFlag is a flag with type Generic -type GenericFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value Generic -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f GenericFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f GenericFlag) GetName() string { - return f.Name -} - -// Generic looks up the value of a local GenericFlag, returns -// nil if not found -func (c *Context) Generic(name string) interface{} { - return lookupGeneric(name, c.flagSet) -} - -// GlobalGeneric looks up the value of a global GenericFlag, returns -// nil if not found -func (c *Context) GlobalGeneric(name string) interface{} { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupGeneric(name, fs) - } - return nil -} - -func lookupGeneric(name string, set *flag.FlagSet) interface{} { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value, error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Int64Flag is a flag with type int64 -type Int64Flag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value int64 - Destination *int64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Int64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Int64Flag) GetName() string { - return f.Name -} - -// Int64 looks up the value of a local Int64Flag, returns -// 0 if not found -func (c *Context) Int64(name string) int64 { - return lookupInt64(name, c.flagSet) -} - -// GlobalInt64 looks up the value of a global Int64Flag, returns -// 0 if not found -func (c *Context) GlobalInt64(name string) int64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt64(name, fs) - } - return 0 -} - -func lookupInt64(name string, set *flag.FlagSet) int64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// IntFlag is a flag with type int -type IntFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value int - Destination *int -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f IntFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f IntFlag) GetName() string { - return f.Name -} - -// Int looks up the value of a local IntFlag, returns -// 0 if not found -func (c *Context) Int(name string) int { - return lookupInt(name, c.flagSet) -} - -// GlobalInt looks up the value of a global IntFlag, returns -// 0 if not found -func (c *Context) GlobalInt(name string) int { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt(name, fs) - } - return 0 -} - -func lookupInt(name string, set *flag.FlagSet) int { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return int(parsed) - } - return 0 -} - -// IntSliceFlag is a flag with type *IntSlice -type IntSliceFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value *IntSlice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f IntSliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f IntSliceFlag) GetName() string { - return f.Name -} - -// IntSlice looks up the value of a local IntSliceFlag, returns -// nil if not found -func (c *Context) IntSlice(name string) []int { - return lookupIntSlice(name, c.flagSet) -} - -// GlobalIntSlice looks up the value of a global IntSliceFlag, returns -// nil if not found -func (c *Context) GlobalIntSlice(name string) []int { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupIntSlice(name, fs) - } - return nil -} - -func lookupIntSlice(name string, set *flag.FlagSet) []int { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Int64SliceFlag is a flag with type *Int64Slice -type Int64SliceFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value *Int64Slice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Int64SliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Int64SliceFlag) GetName() string { - return f.Name -} - -// Int64Slice looks up the value of a local Int64SliceFlag, returns -// nil if not found -func (c *Context) Int64Slice(name string) []int64 { - return lookupInt64Slice(name, c.flagSet) -} - -// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns -// nil if not found -func (c *Context) GlobalInt64Slice(name string) []int64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt64Slice(name, fs) - } - return nil -} - -func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// StringFlag is a flag with type string -type StringFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value string - Destination *string -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f StringFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f StringFlag) GetName() string { - return f.Name -} - -// String looks up the value of a local StringFlag, returns -// "" if not found -func (c *Context) String(name string) string { - return lookupString(name, c.flagSet) -} - -// GlobalString looks up the value of a global StringFlag, returns -// "" if not found -func (c *Context) GlobalString(name string) string { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupString(name, fs) - } - return "" -} - -func lookupString(name string, set *flag.FlagSet) string { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value.String(), error(nil) - if err != nil { - return "" - } - return parsed - } - return "" -} - -// StringSliceFlag is a flag with type *StringSlice -type StringSliceFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value *StringSlice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f StringSliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f StringSliceFlag) GetName() string { - return f.Name -} - -// StringSlice looks up the value of a local StringSliceFlag, returns -// nil if not found -func (c *Context) StringSlice(name string) []string { - return lookupStringSlice(name, c.flagSet) -} - -// GlobalStringSlice looks up the value of a global StringSliceFlag, returns -// nil if not found -func (c *Context) GlobalStringSlice(name string) []string { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupStringSlice(name, fs) - } - return nil -} - -func lookupStringSlice(name string, set *flag.FlagSet) []string { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Uint64Flag is a flag with type uint64 -type Uint64Flag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value uint64 - Destination *uint64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Uint64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Uint64Flag) GetName() string { - return f.Name -} - -// Uint64 looks up the value of a local Uint64Flag, returns -// 0 if not found -func (c *Context) Uint64(name string) uint64 { - return lookupUint64(name, c.flagSet) -} - -// GlobalUint64 looks up the value of a global Uint64Flag, returns -// 0 if not found -func (c *Context) GlobalUint64(name string) uint64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupUint64(name, fs) - } - return 0 -} - -func lookupUint64(name string, set *flag.FlagSet) uint64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// UintFlag is a flag with type uint -type UintFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value uint - Destination *uint -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f UintFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f UintFlag) GetName() string { - return f.Name -} - -// Uint looks up the value of a local UintFlag, returns -// 0 if not found -func (c *Context) Uint(name string) uint { - return lookupUint(name, c.flagSet) -} - -// GlobalUint looks up the value of a global UintFlag, returns -// 0 if not found -func (c *Context) GlobalUint(name string) uint { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupUint(name, fs) - } - return 0 -} - -func lookupUint(name string, set *flag.FlagSet) uint { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return uint(parsed) - } - return 0 -} diff --git a/vendor/github.com/codegangsta/cli/funcs.go b/vendor/github.com/codegangsta/cli/funcs.go deleted file mode 100644 index cba5e6cb0..000000000 --- a/vendor/github.com/codegangsta/cli/funcs.go +++ /dev/null @@ -1,28 +0,0 @@ -package cli - -// BashCompleteFunc is an action to execute when the bash-completion flag is set -type BashCompleteFunc func(*Context) - -// BeforeFunc is an action to execute before any subcommands are run, but after -// the context is ready if a non-nil error is returned, no subcommands are run -type BeforeFunc func(*Context) error - -// AfterFunc is an action to execute after any subcommands are run, but after the -// subcommand has finished it is run even if Action() panics -type AfterFunc func(*Context) error - -// ActionFunc is the action to execute when no subcommands are specified -type ActionFunc func(*Context) error - -// CommandNotFoundFunc is executed if the proper command cannot be found -type CommandNotFoundFunc func(*Context, string) - -// OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying -// customized usage error messages. This function is able to replace the -// original error messages. If this function is not set, the "Incorrect usage" -// is displayed and the execution is interrupted. -type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error - -// FlagStringFunc is used by the help generation to display a flag, which is -// expected to be a single line. -type FlagStringFunc func(Flag) string diff --git a/vendor/github.com/codegangsta/cli/help.go b/vendor/github.com/codegangsta/cli/help.go deleted file mode 100644 index 57ec98d58..000000000 --- a/vendor/github.com/codegangsta/cli/help.go +++ /dev/null @@ -1,338 +0,0 @@ -package cli - -import ( - "fmt" - "io" - "os" - "strings" - "text/tabwriter" - "text/template" -) - -// AppHelpTemplate is the text template for the Default help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var AppHelpTemplate = `NAME: - {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} - -USAGE: - {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} - -VERSION: - {{.Version}}{{end}}{{end}}{{if .Description}} - -DESCRIPTION: - {{.Description}}{{end}}{{if len .Authors}} - -AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: - {{range $index, $author := .Authors}}{{if $index}} - {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} - -COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{end}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} - -GLOBAL OPTIONS: - {{range $index, $option := .VisibleFlags}}{{if $index}} - {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} - -COPYRIGHT: - {{.Copyright}}{{end}} -` - -// CommandHelpTemplate is the text template for the command help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var CommandHelpTemplate = `NAME: - {{.HelpName}} - {{.Usage}} - -USAGE: - {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} - -CATEGORY: - {{.Category}}{{end}}{{if .Description}} - -DESCRIPTION: - {{.Description}}{{end}}{{if .VisibleFlags}} - -OPTIONS: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}} -` - -// SubcommandHelpTemplate is the text template for the subcommand help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var SubcommandHelpTemplate = `NAME: - {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} - -USAGE: - {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} - -COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{end}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} -{{end}}{{if .VisibleFlags}} -OPTIONS: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}} -` - -var helpCommand = Command{ - Name: "help", - Aliases: []string{"h"}, - Usage: "Shows a list of commands or help for one command", - ArgsUsage: "[command]", - Action: func(c *Context) error { - args := c.Args() - if args.Present() { - return ShowCommandHelp(c, args.First()) - } - - ShowAppHelp(c) - return nil - }, -} - -var helpSubcommand = Command{ - Name: "help", - Aliases: []string{"h"}, - Usage: "Shows a list of commands or help for one command", - ArgsUsage: "[command]", - Action: func(c *Context) error { - args := c.Args() - if args.Present() { - return ShowCommandHelp(c, args.First()) - } - - return ShowSubcommandHelp(c) - }, -} - -// Prints help for the App or Command -type helpPrinter func(w io.Writer, templ string, data interface{}) - -// Prints help for the App or Command with custom template function. -type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) - -// HelpPrinter is a function that writes the help output. If not set a default -// is used. The function signature is: -// func(w io.Writer, templ string, data interface{}) -var HelpPrinter helpPrinter = printHelp - -// HelpPrinterCustom is same as HelpPrinter but -// takes a custom function for template function map. -var HelpPrinterCustom helpPrinterCustom = printHelpCustom - -// VersionPrinter prints the version for the App -var VersionPrinter = printVersion - -// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. -func ShowAppHelpAndExit(c *Context, exitCode int) { - ShowAppHelp(c) - os.Exit(exitCode) -} - -// ShowAppHelp is an action that displays the help. -func ShowAppHelp(c *Context) (err error) { - if c.App.CustomAppHelpTemplate == "" { - HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) - return - } - customAppData := func() map[string]interface{} { - if c.App.ExtraInfo == nil { - return nil - } - return map[string]interface{}{ - "ExtraInfo": c.App.ExtraInfo, - } - } - HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData()) - return nil -} - -// DefaultAppComplete prints the list of subcommands as the default app completion method -func DefaultAppComplete(c *Context) { - for _, command := range c.App.Commands { - if command.Hidden { - continue - } - for _, name := range command.Names() { - fmt.Fprintln(c.App.Writer, name) - } - } -} - -// ShowCommandHelpAndExit - exits with code after showing help -func ShowCommandHelpAndExit(c *Context, command string, code int) { - ShowCommandHelp(c, command) - os.Exit(code) -} - -// ShowCommandHelp prints help for the given command -func ShowCommandHelp(ctx *Context, command string) error { - // show the subcommand help for a command with subcommands - if command == "" { - HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) - return nil - } - - for _, c := range ctx.App.Commands { - if c.HasName(command) { - if c.CustomHelpTemplate != "" { - HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil) - } else { - HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) - } - return nil - } - } - - if ctx.App.CommandNotFound == nil { - return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3) - } - - ctx.App.CommandNotFound(ctx, command) - return nil -} - -// ShowSubcommandHelp prints help for the given subcommand -func ShowSubcommandHelp(c *Context) error { - return ShowCommandHelp(c, c.Command.Name) -} - -// ShowVersion prints the version number of the App -func ShowVersion(c *Context) { - VersionPrinter(c) -} - -func printVersion(c *Context) { - fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) -} - -// ShowCompletions prints the lists of commands within a given context -func ShowCompletions(c *Context) { - a := c.App - if a != nil && a.BashComplete != nil { - a.BashComplete(c) - } -} - -// ShowCommandCompletions prints the custom completions for a given command -func ShowCommandCompletions(ctx *Context, command string) { - c := ctx.App.Command(command) - if c != nil && c.BashComplete != nil { - c.BashComplete(ctx) - } -} - -func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { - funcMap := template.FuncMap{ - "join": strings.Join, - } - if customFunc != nil { - for key, value := range customFunc { - funcMap[key] = value - } - } - - w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) - t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) - err := t.Execute(w, data) - if err != nil { - // If the writer is closed, t.Execute will fail, and there's nothing - // we can do to recover. - if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { - fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) - } - return - } - w.Flush() -} - -func printHelp(out io.Writer, templ string, data interface{}) { - printHelpCustom(out, templ, data, nil) -} - -func checkVersion(c *Context) bool { - found := false - if VersionFlag.GetName() != "" { - eachName(VersionFlag.GetName(), func(name string) { - if c.GlobalBool(name) || c.Bool(name) { - found = true - } - }) - } - return found -} - -func checkHelp(c *Context) bool { - found := false - if HelpFlag.GetName() != "" { - eachName(HelpFlag.GetName(), func(name string) { - if c.GlobalBool(name) || c.Bool(name) { - found = true - } - }) - } - return found -} - -func checkCommandHelp(c *Context, name string) bool { - if c.Bool("h") || c.Bool("help") { - ShowCommandHelp(c, name) - return true - } - - return false -} - -func checkSubcommandHelp(c *Context) bool { - if c.Bool("h") || c.Bool("help") { - ShowSubcommandHelp(c) - return true - } - - return false -} - -func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { - if !a.EnableBashCompletion { - return false, arguments - } - - pos := len(arguments) - 1 - lastArg := arguments[pos] - - if lastArg != "--"+BashCompletionFlag.GetName() { - return false, arguments - } - - return true, arguments[:pos] -} - -func checkCompletions(c *Context) bool { - if !c.shellComplete { - return false - } - - if args := c.Args(); args.Present() { - name := args.First() - if cmd := c.App.Command(name); cmd != nil { - // let the command handle the completion - return false - } - } - - ShowCompletions(c) - return true -} - -func checkCommandCompletions(c *Context, name string) bool { - if !c.shellComplete { - return false - } - - ShowCommandCompletions(c, name) - return true -} diff --git a/vendor/github.com/vulcand/route/LICENSE b/vendor/github.com/vulcand/route/LICENSE deleted file mode 100644 index e06d20818..000000000 --- a/vendor/github.com/vulcand/route/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -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. - diff --git a/vendor/github.com/vulcand/route/iter.go b/vendor/github.com/vulcand/route/iter.go deleted file mode 100644 index b41685e49..000000000 --- a/vendor/github.com/vulcand/route/iter.go +++ /dev/null @@ -1,88 +0,0 @@ -package route - -import ( - "fmt" -) - -// charPos stores the position in the iterator -type charPos struct { - i int - si int -} - -// charIter is a iterator over sequence of strings, returns byte-by-byte characters in string by string -type charIter struct { - i int // position in the current string - si int // position in the array of strings - - seq []string // sequence of strings, e.g. ["GET", "/path"] - sep []byte // every string in the sequence has an associated separator used for trie matching, e.g. path uses '/' for separator - // so sequence ["a.host", "/path "]has acoompanying separators ['.', '/'] -} - -func newIter(seq []string, sep []byte) *charIter { - return &charIter{ - i: 0, - si: 0, - seq: seq, - sep: sep, - } -} - -func (r *charIter) level() int { - return r.si -} - -func (r *charIter) String() string { - if r.isEnd() { - return "" - } - return fmt.Sprintf("<%d:%v>", r.i, r.seq[r.si]) -} - -func (r *charIter) isEnd() bool { - return len(r.seq) == 0 || // no data at all - (r.si >= len(r.seq)-1 && r.i >= len(r.seq[r.si])) || // we are at the last char of last seq - (len(r.seq[r.si]) == 0) // empty input -} - -func (r *charIter) position() charPos { - return charPos{i: r.i, si: r.si} -} - -func (r *charIter) setPosition(p charPos) { - r.i = p.i - r.si = p.si -} - -func (r *charIter) pushBack() { - if r.i == 0 && r.si == 0 { // this is start - return - } else if r.i == 0 && r.si != 0 { // this is start of the next string - r.si-- - r.i = len(r.seq[r.si]) - 1 - return - } - r.i-- -} - -// next returns current byte in the sequence, separator corresponding to that byte, and boolean idicator of whether it's the end of the sequence -func (r *charIter) next() (byte, byte, bool) { - // we have reached the last string in the index, end - if r.isEnd() { - return 0, 0, false - } - - b := r.seq[r.si][r.i] - sep := r.sep[r.si] - r.i++ - - // current string index exceeded the last char of the current string - // move to the next string if it's present - if r.i >= len(r.seq[r.si]) && r.si < len(r.seq)-1 { - r.si++ - r.i = 0 - } - - return b, sep, true -} diff --git a/vendor/github.com/vulcand/route/mapper.go b/vendor/github.com/vulcand/route/mapper.go deleted file mode 100644 index 2e79b1362..000000000 --- a/vendor/github.com/vulcand/route/mapper.go +++ /dev/null @@ -1,186 +0,0 @@ -package route - -import ( - "net/http" - "strings" -) - -// requestMapper maps the request to string e.g. maps request to it's hostname, or request to header -type requestMapper interface { - // separator returns the separator that makes sense for this request, e.g. / for urls or . for domains - separator() byte - // equals returns the equivalent mapper if the two mappers are equivalent, e.g. map to the same sequence - // mappers are also equivalent if one mapper is subset of another, e.g. combined mapper (host, path) is equivalent of (host) mapper - equivalent(requestMapper) requestMapper - // mapRequest maps request to string, e.g. request to it's URL path - mapRequest(r *http.Request) string - // newIter returns the iterator instead of string for stream matchers - newIter(r *http.Request) *charIter -} - -type methodMapper struct { -} - -func (m *methodMapper) separator() byte { - return methodSep -} - -func (m *methodMapper) equivalent(o requestMapper) requestMapper { - _, ok := o.(*methodMapper) - if ok { - return m - } - return nil -} - -func (m *methodMapper) mapRequest(r *http.Request) string { - return r.Method -} - -func (m *methodMapper) newIter(r *http.Request) *charIter { - return newIter([]string{m.mapRequest(r)}, []byte{m.separator()}) -} - -type pathMapper struct { -} - -func (m *pathMapper) separator() byte { - return pathSep -} - -func (p *pathMapper) equivalent(o requestMapper) requestMapper { - _, ok := o.(*pathMapper) - if ok { - return p - } - return nil -} - -func (p *pathMapper) newIter(r *http.Request) *charIter { - return newIter([]string{p.mapRequest(r)}, []byte{p.separator()}) -} - -func (p *pathMapper) mapRequest(r *http.Request) string { - return rawPath(r) -} - -type hostMapper struct { -} - -func (p *hostMapper) equivalent(o requestMapper) requestMapper { - _, ok := o.(*hostMapper) - if ok { - return p - } - return nil -} - -func (m *hostMapper) separator() byte { - return domainSep -} - -func (h *hostMapper) mapRequest(r *http.Request) string { - return strings.Split(strings.ToLower(r.Host), ":")[0] -} - -func (p *hostMapper) newIter(r *http.Request) *charIter { - return newIter([]string{p.mapRequest(r)}, []byte{p.separator()}) -} - -type headerMapper struct { - header string -} - -func (h *headerMapper) equivalent(o requestMapper) requestMapper { - hm, ok := o.(*headerMapper) - if ok && hm.header == h.header { - return h - } - return nil -} - -func (m *headerMapper) separator() byte { - return headerSep -} - -func (h *headerMapper) mapRequest(r *http.Request) string { - return r.Header.Get(h.header) -} - -func (h *headerMapper) newIter(r *http.Request) *charIter { - return newIter([]string{h.mapRequest(r)}, []byte{h.separator()}) -} - -type seqMapper struct { - seq []requestMapper -} - -func newSeqMapper(seq ...requestMapper) *seqMapper { - var out []requestMapper - for _, s := range seq { - switch m := s.(type) { - case *seqMapper: - out = append(out, m.seq...) - default: - out = append(out, s) - } - } - return &seqMapper{seq: out} -} - -func (s *seqMapper) newIter(r *http.Request) *charIter { - out := make([]string, len(s.seq)) - for i := range s.seq { - out[i] = s.seq[i].mapRequest(r) - } - seps := make([]byte, len(s.seq)) - for i := range s.seq { - seps[i] = s.seq[i].separator() - } - return newIter(out, seps) -} - -func (s *seqMapper) mapRequest(r *http.Request) string { - out := make([]string, len(s.seq)) - for i := range s.seq { - out[i] = s.seq[i].mapRequest(r) - } - return strings.Join(out, "") -} - -func (s *seqMapper) separator() byte { - return s.seq[0].separator() -} - -func (s *seqMapper) equivalent(o requestMapper) requestMapper { - so, ok := o.(*seqMapper) - if !ok { - return nil - } - - var longer, shorter *seqMapper - if len(s.seq) > len(so.seq) { - longer = s - shorter = so - } else { - longer = so - shorter = s - } - for i, _ := range longer.seq { - // shorter is subset of longer, return longer sequence mapper - if i >= len(shorter.seq)-1 { - return longer - } - if longer.seq[i].equivalent(shorter.seq[i]) == nil { - return nil - } - } - return longer -} - -const ( - pathSep = '/' - domainSep = '.' - headerSep = '/' - methodSep = ' ' -) diff --git a/vendor/github.com/vulcand/route/matcher.go b/vendor/github.com/vulcand/route/matcher.go deleted file mode 100644 index fbc026133..000000000 --- a/vendor/github.com/vulcand/route/matcher.go +++ /dev/null @@ -1,155 +0,0 @@ -package route - -import ( - "fmt" - "net/http" - "regexp" - "strings" -) - -type matcher interface { - match(*http.Request) *match - setMatch(match *match) - - canMerge(matcher) bool - merge(matcher) (matcher, error) - - canChain(matcher) bool - chain(matcher) (matcher, error) -} - -func hostTrieMatcher(hostname string) (matcher, error) { - return newTrieMatcher(strings.ToLower(hostname), &hostMapper{}, &match{}) -} - -func hostRegexpMatcher(hostname string) (matcher, error) { - return newRegexpMatcher(strings.ToLower(hostname), &hostMapper{}, &match{}) -} - -func methodTrieMatcher(method string) (matcher, error) { - return newTrieMatcher(method, &methodMapper{}, &match{}) -} - -func methodRegexpMatcher(method string) (matcher, error) { - return newRegexpMatcher(method, &methodMapper{}, &match{}) -} - -func pathTrieMatcher(path string) (matcher, error) { - return newTrieMatcher(path, &pathMapper{}, &match{}) -} - -func pathRegexpMatcher(path string) (matcher, error) { - return newRegexpMatcher(path, &pathMapper{}, &match{}) -} - -func headerTrieMatcher(name, value string) (matcher, error) { - return newTrieMatcher(value, &headerMapper{header: name}, &match{}) -} - -func headerRegexpMatcher(name, value string) (matcher, error) { - return newRegexpMatcher(value, &headerMapper{header: name}, &match{}) -} - -type match struct { - val interface{} -} - -type andMatcher struct { - a matcher - b matcher -} - -func newAndMatcher(a, b matcher) matcher { - if a.canChain(b) { - m, err := a.chain(b) - if err == nil { - return m - } - } - return &andMatcher{ - a: a, b: b, - } -} - -func (a *andMatcher) canChain(matcher) bool { - return false -} - -func (a *andMatcher) chain(matcher) (matcher, error) { - return nil, fmt.Errorf("not supported") -} - -func (a *andMatcher) String() string { - return fmt.Sprintf("andMatcher(%v, %v)", a.a, a.b) -} - -func (a *andMatcher) setMatch(m *match) { - a.a.setMatch(m) - a.b.setMatch(m) -} - -func (a *andMatcher) canMerge(o matcher) bool { - return false -} - -func (a *andMatcher) merge(o matcher) (matcher, error) { - return nil, fmt.Errorf("Method not supported") -} - -func (a *andMatcher) match(req *http.Request) *match { - result := a.a.match(req) - if result == nil { - return nil - } - return a.b.match(req) -} - -// Regular expression matcher, takes a regular expression and requestMapper -type regexpMatcher struct { - // Uses this mapper to extract a string from a request to match against - mapper requestMapper - // Compiled regular expression - expr *regexp.Regexp - // match result - result *match -} - -func (r *regexpMatcher) canChain(matcher) bool { - return false -} - -func (r *regexpMatcher) chain(matcher) (matcher, error) { - return nil, fmt.Errorf("not supported") -} - -func (m *regexpMatcher) String() string { - return fmt.Sprintf("regexpMatcher(%v)", m.expr) -} - -func (m *regexpMatcher) setMatch(result *match) { - m.result = result -} - -func newRegexpMatcher(expr string, mapper requestMapper, m *match) (matcher, error) { - r, err := regexp.Compile(expr) - - if err != nil { - return nil, fmt.Errorf("Bad regular expression: %s %s", expr, err) - } - return ®expMatcher{expr: r, mapper: mapper, result: m}, nil -} - -func (m *regexpMatcher) canMerge(matcher) bool { - return false -} - -func (m *regexpMatcher) merge(matcher) (matcher, error) { - return nil, fmt.Errorf("Method not supported") -} - -func (m *regexpMatcher) match(req *http.Request) *match { - if m.expr.MatchString(m.mapper.mapRequest(req)) { - return m.result - } - return nil -} diff --git a/vendor/github.com/vulcand/route/mux.go b/vendor/github.com/vulcand/route/mux.go deleted file mode 100644 index 16498cc62..000000000 --- a/vendor/github.com/vulcand/route/mux.go +++ /dev/null @@ -1,74 +0,0 @@ -package route - -import ( - "fmt" - "net/http" -) - -// Mux implements router compatible with http.Handler -type Mux struct { - // NotFound sets handler for routes that are not found - notFound http.Handler - router Router -} - -// NewMux returns new Mux router -func NewMux() *Mux { - return &Mux{ - router: New(), - notFound: ¬Found{}, - } -} - -// Handle adds http handler for route expression -func (m *Mux) Handle(expr string, handler http.Handler) error { - return m.router.UpsertRoute(expr, handler) -} - -// Handle adds http handler function for route expression -func (m *Mux) HandleFunc(expr string, handler func(http.ResponseWriter, *http.Request)) error { - return m.Handle(expr, http.HandlerFunc(handler)) -} - -func (m *Mux) Remove(expr string) error { - return m.router.RemoveRoute(expr) -} - -// ServeHTTP routes the request and passes it to handler -func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { - h, err := m.router.Route(r) - if err != nil || h == nil { - m.notFound.ServeHTTP(w, r) - return - } - h.(http.Handler).ServeHTTP(w, r) -} - -func (m *Mux) SetNotFound(n http.Handler) error { - if n == nil { - return fmt.Errorf("Not Found handler cannot be nil. Operation rejected.") - } - m.notFound = n - return nil -} - -func (m *Mux) GetNotFound() http.Handler { - return m.notFound -} - -func (m *Mux) IsValid(expr string) bool { - return IsValid(expr) -} - -// NotFound is a generic http.Handler for request -type notFound struct { -} - -// ServeHTTP returns a simple 404 Not found response -func (notFound) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusNotFound) - fmt.Fprint(w, "Not found") - -} - diff --git a/vendor/github.com/vulcand/route/parse.go b/vendor/github.com/vulcand/route/parse.go deleted file mode 100644 index 06edea930..000000000 --- a/vendor/github.com/vulcand/route/parse.go +++ /dev/null @@ -1,47 +0,0 @@ -package route - -import ( - "fmt" - - "github.com/vulcand/predicate" -) - -// IsValid checks whether expression is valid -func IsValid(expr string) bool { - _, err := parse(expr, &match{}) - return err == nil -} - -func parse(expression string, result *match) (matcher, error) { - p, err := predicate.NewParser(predicate.Def{ - Functions: map[string]interface{}{ - "Host": hostTrieMatcher, - "HostRegexp": hostRegexpMatcher, - - "Path": pathTrieMatcher, - "PathRegexp": pathRegexpMatcher, - - "Method": methodTrieMatcher, - "MethodRegexp": methodRegexpMatcher, - - "Header": headerTrieMatcher, - "HeaderRegexp": headerRegexpMatcher, - }, - Operators: predicate.Operators{ - AND: newAndMatcher, - }, - }) - if err != nil { - return nil, err - } - out, err := p.Parse(expression) - if err != nil { - return nil, err - } - m, ok := out.(matcher) - if !ok { - return nil, fmt.Errorf("unknown result type: %T", out) - } - m.setMatch(result) - return m, nil -} diff --git a/vendor/github.com/vulcand/route/router.go b/vendor/github.com/vulcand/route/router.go deleted file mode 100644 index 255e49104..000000000 --- a/vendor/github.com/vulcand/route/router.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -package route provides http package-compatible routing library. It can route http requests by hostname, method, path and headers. - -Route defines simple language for matching requests based on Go syntax. Route provides series of matchers that follow the syntax: - - - Matcher("value") // matches value using trie - Matcher(".value") // uses trie-based matching for a.value and b.value - MatcherRegexp(".*value") // uses regexp-based matching - -Host matcher: - - Host(".localhost") // trie-based matcher for a.localhost, b.localhost, etc. - HostRegexp(".*localhost") // regexp based matcher - -Path matcher: - - Path("/hello/") // trie-based matcher for raw request path - PathRegexp("/hello/.*") // regexp-based matcher for raw request path - -Method matcher: - - Method("GET") // trie-based matcher for request method - MethodRegexp("POST|PUT") // regexp based matcher for request method - -Header matcher: - - Header("Content-Type", "application/") // trie-based matcher for headers - HeaderRegexp("Content-Type", "application/.*") // regexp based matcher for headers - -Matchers can be combined using && operator: - - Host("localhost") && Method("POST") && Path("/v1") - -Route library will join the trie-based matchers into one trie matcher when possible, for example: - - Host("localhost") && Method("POST") && Path("/v1") - Host("localhost") && Method("GET") && Path("/v2") - -Will be combined into one trie for performance. If you add a third route: - - Host("localhost") && Method("GET") && PathRegexp("/v2/.*") - -It wont be joined ito the trie, and would be matched separatedly instead. -*/ -package route - -import ( - "fmt" - "net/http" - "sort" - "sync" -) - -// Router implements http request routing and operations. It is a generic router not conforming to http.Handler interface, to get a handler -// conforming to http.Handler interface, use Mux router instead. -type Router interface { - // GetRoute returns a route by a given expression, returns nil if expresison is not found - GetRoute(string) interface{} - - // AddRoute adds a route to match by expression, returns error if the expression already defined, or route expression is incorrect - AddRoute(string, interface{}) error - - // RemoveRoute removes a route for a given expression - RemoveRoute(string) error - - // UpsertRoute updates an existing route or adds a new route by given expression - UpsertRoute(string, interface{}) error - - // Route takes a request and matches it against requests, returns matched route in case if found, nil if there's no matching route or error in case of internal error. - Route(*http.Request) (interface{}, error) -} - -type router struct { - mutex *sync.RWMutex - matchers []matcher - routes map[string]*match -} - -// New creates a new Router instance -func New() Router { - return &router{ - mutex: &sync.RWMutex{}, - routes: make(map[string]*match), - } -} - -func (e *router) GetRoute(expr string) interface{} { - e.mutex.RLock() - defer e.mutex.RUnlock() - - res, ok := e.routes[expr] - if ok { - return res.val - } - return nil -} - -func (e *router) AddRoute(expr string, val interface{}) error { - e.mutex.Lock() - defer e.mutex.Unlock() - - if _, ok := e.routes[expr]; ok { - return fmt.Errorf("Expression '%s' already exists", expr) - } - result := &match{val: val} - if _, err := parse(expr, result); err != nil { - return err - } - e.routes[expr] = result - if err := e.compile(); err != nil { - delete(e.routes, expr) - return err - } - return nil -} - -func (e *router) UpsertRoute(expr string, val interface{}) error { - e.mutex.Lock() - defer e.mutex.Unlock() - - result := &match{val: val} - if _, err := parse(expr, result); err != nil { - return err - } - prev, existed := e.routes[expr] - - e.routes[expr] = result - if err := e.compile(); err != nil { - if existed { - e.routes[expr] = prev - } else { - delete(e.routes, expr) - } - return err - } - return nil -} - -func (e *router) compile() error { - var exprs = []string{} - for expr, _ := range e.routes { - exprs = append(exprs, expr) - } - sort.Sort(sort.Reverse(sort.StringSlice(exprs))) - - matchers := []matcher{} - i := 0 - for _, expr := range exprs { - result := e.routes[expr] - matcher, err := parse(expr, result) - if err != nil { - return err - } - - // Merge the previous and new matcher if that's possible - if i > 0 && matchers[i-1].canMerge(matcher) { - m, err := matchers[i-1].merge(matcher) - if err != nil { - return err - } - matchers[i-1] = m - } else { - matchers = append(matchers, matcher) - i += 1 - } - } - - e.matchers = matchers - return nil -} - -func (e *router) RemoveRoute(expr string) error { - e.mutex.Lock() - defer e.mutex.Unlock() - - delete(e.routes, expr) - return e.compile() -} - -func (e *router) Route(req *http.Request) (interface{}, error) { - e.mutex.RLock() - defer e.mutex.RUnlock() - - if len(e.matchers) == 0 { - return nil, nil - } - - for _, m := range e.matchers { - if l := m.match(req); l != nil { - return l.val, nil - } - } - return nil, nil -} diff --git a/vendor/github.com/vulcand/route/trie.go b/vendor/github.com/vulcand/route/trie.go deleted file mode 100644 index fe4c517ab..000000000 --- a/vendor/github.com/vulcand/route/trie.go +++ /dev/null @@ -1,476 +0,0 @@ -package route - -import ( - "bytes" - "fmt" - "net/http" - "regexp" - "strings" - "unicode" -) - -// Regular expression to match url parameters -var reParam *regexp.Regexp - -func init() { - reParam = regexp.MustCompile("^<([^>]+)>") -} - -// Trie http://en.wikipedia.org/wiki/Trie for url matching with support of named parameters -type trie struct { - root *trieNode - // mapper takes the request and returns sequence that can be matched - mapper requestMapper -} - -func (t *trie) canChain(o matcher) bool { - _, ok := o.(*trie) - return ok -} - -func (t *trie) chain(o matcher) (matcher, error) { - to, ok := o.(*trie) - if !ok { - return nil, fmt.Errorf("can chain only with other trie") - } - m := t.root.findMatchNode() - m.matches = nil - m.children = []*trieNode{to.root} - t.root.setLevel(-1) - return &trie{ - root: t.root, - mapper: newSeqMapper(t.mapper, to.mapper), - }, nil -} - -func (t *trie) String() string { - return fmt.Sprintf("trieMatcher()") -} - -// Takes the expression with url and the node that corresponds to this expression and returns parsed trie -func newTrieMatcher(expression string, mapper requestMapper, result *match) (*trie, error) { - t := &trie{ - mapper: mapper, - } - t.root = &trieNode{trie: t} - if len(expression) == 0 { - return nil, fmt.Errorf("Empty URL expression") - } - err := t.root.parseExpression(-1, expression, result) - if err != nil { - return nil, err - } - return t, nil -} - -func (t *trie) setMatch(result *match) { - t.root.setMatch(result) -} - -// Tries can merge with other tries -func (t *trie) canMerge(m matcher) bool { - ot, ok := m.(*trie) - return ok && t.mapper.equivalent(ot.mapper) != nil -} - -// Merge takes the other trie and modifies itself to match the passed trie as well. -// Note that trie passed as a parameter can be only simple trie without multiple branches per node, e.g. a->b->c-> -// Trie on the left is "accumulating" trie that grows. -func (p *trie) merge(m matcher) (matcher, error) { - other, ok := m.(*trie) - if !ok { - return nil, fmt.Errorf("Can't merge %T and %T", p, m) - } - mapper := p.mapper.equivalent(other.mapper) - if mapper == nil { - return nil, fmt.Errorf("Can't merge %T and %T", p, m) - } - - root, err := p.root.merge(other.root) - if err != nil { - return nil, err - } - return &trie{root: root, mapper: mapper}, nil -} - -// Takes the request and returns the location if the request path matches any of it's paths -// returns nil if none of the requests matches -func (p *trie) match(r *http.Request) *match { - if p.root == nil { - return nil - } - return p.root.match(p.mapper.newIter(r)) -} - -type trieNode struct { - trie *trie - // Matching character, can be empty in case if it's a root node - // or node with a pattern matcher - char byte - // Optional children of this node, can be empty if it's a leaf node - children []*trieNode - // If present, means that this node is a pattern matcher - patternMatcher patternMatcher - // If present it means this node contains potential match for a request, and this is a leaf node. - matches []*match - // For chained tries matching different parts of the request levels would increase for next chained trie nodes - level int -} - -func (e *trieNode) setMatch(m *match) { - n := e.findMatchNode() - n.matches = []*match{m} -} - -func (e *trieNode) setLevel(level int) { - if e.isRoot() { - level++ - } - e.level = level - if len(e.matches) != 0 { - return - } - // Check for the match in child nodes - for _, c := range e.children { - c.setLevel(level) - } -} - -func (e *trieNode) findMatchNode() *trieNode { - if len(e.matches) != 0 { - return e - } - // Check for the match in child nodes - for _, c := range e.children { - if n := c.findMatchNode(); n != nil { - return n - } - } - return nil -} - -func (e *trieNode) isMatching() bool { - return len(e.matches) != 0 -} - -func (e *trieNode) isRoot() bool { - return e.char == byte(0) && e.patternMatcher == nil -} - -func (e *trieNode) isPatternMatcher() bool { - return e.patternMatcher != nil -} - -func (e *trieNode) isCharMatcher() bool { - return e.char != 0 -} - -func (e *trieNode) String() string { - self := "" - if e.patternMatcher != nil { - self = e.patternMatcher.String() - } else { - self = fmt.Sprintf("%c", e.char) - } - if e.isMatching() { - return fmt.Sprintf("match(%d:%s)", e.level, self) - } else if e.isRoot() { - return fmt.Sprintf("root(%d)", e.level) - } else { - return fmt.Sprintf("node(%d:%s)", e.level, self) - } -} - -func (e *trieNode) equals(o *trieNode) bool { - return (e.level == o.level) && // we can merge nodes that are on the same level to avoid merges for different subtrie parts - (e.char == o.char) && // chars are equal - (e.patternMatcher == nil && o.patternMatcher == nil) || // both nodes have no matchers - ((e.patternMatcher != nil && o.patternMatcher != nil) && e.patternMatcher.equals(o.patternMatcher)) // both nodes have equal matchers -} - -func (e *trieNode) merge(o *trieNode) (*trieNode, error) { - children := make([]*trieNode, 0, len(e.children)) - merged := make(map[*trieNode]bool) - - // First, find the nodes with similar keys and merge them - for _, c := range e.children { - for _, c2 := range o.children { - // The nodes are equivalent, so we can merge them - if c.equals(c2) { - m, err := c.merge(c2) - if err != nil { - return nil, err - } - merged[c] = true - merged[c2] = true - children = append(children, m) - } - } - } - - // Next, append the keys that haven't been merged - for _, c := range e.children { - if !merged[c] { - children = append(children, c) - } - } - - for _, c := range o.children { - if !merged[c] { - children = append(children, c) - } - } - - return &trieNode{ - level: e.level, - trie: e.trie, - char: e.char, - children: children, - patternMatcher: e.patternMatcher, - matches: append(e.matches, o.matches...), - }, nil -} - -func (p *trieNode) parseExpression(offset int, pattern string, m *match) error { - // We are the last element, so we are the matching node - if offset >= len(pattern)-1 { - p.matches = []*match{m} - return nil - } - - // There's a next character that exists - patternMatcher, newOffset, err := parsePatternMatcher(offset+1, pattern) - // We have found the matcher, but the syntax or parameters are wrong - if err != nil { - return err - } - // Matcher was found - if patternMatcher != nil { - node := &trieNode{patternMatcher: patternMatcher, trie: p.trie} - p.children = []*trieNode{node} - return node.parseExpression(newOffset-1, pattern, m) - } else { - // Matcher was not found, next node is just a character - node := &trieNode{char: pattern[offset+1], trie: p.trie} - p.children = []*trieNode{node} - return node.parseExpression(offset+1, pattern, m) - } -} - -func parsePatternMatcher(offset int, pattern string) (patternMatcher, int, error) { - if pattern[offset] != '<' { - return nil, -1, nil - } - rest := pattern[offset:] - match := reParam.FindStringSubmatchIndex(rest) - if len(match) == 0 { - return nil, -1, nil - } - // Split parsed matcher parameters separated by : - values := strings.Split(rest[match[2]:match[3]], ":") - - // The common syntax is - matcherType := values[0] - matcherArgs := values[1:] - - // In case if there's only one is implicitly converted to - if len(values) == 1 { - matcherType = "string" - matcherArgs = values - } - matcher, err := makeMatcher(matcherType, matcherArgs) - if err != nil { - return nil, offset, err - } - return matcher, offset + match[1], nil -} - -type matchResult struct { - matcher patternMatcher - value interface{} -} - -type patternMatcher interface { - getName() string - match(i *charIter) bool - equals(other patternMatcher) bool - String() string -} - -func makeMatcher(matcherType string, matcherArgs []string) (patternMatcher, error) { - switch matcherType { - case "string": - return newStringMatcher(matcherArgs) - case "int": - return newIntMatcher(matcherArgs) - } - return nil, fmt.Errorf("unsupported matcher: %s", matcherType) -} - -func newStringMatcher(args []string) (patternMatcher, error) { - if len(args) != 1 { - return nil, fmt.Errorf("expected only one parameter - variable name, got: %s", args) - } - return &stringMatcher{name: args[0]}, nil -} - -type stringMatcher struct { - name string -} - -func (s *stringMatcher) String() string { - return fmt.Sprintf("", s.name) -} - -func (s *stringMatcher) getName() string { - return s.name -} - -func (s *stringMatcher) match(i *charIter) bool { - s.grabValue(i) - return true -} - -func (s *stringMatcher) equals(other patternMatcher) bool { - _, ok := other.(*stringMatcher) - return ok && other.getName() == s.getName() -} - -func (s *stringMatcher) grabValue(i *charIter) { - for { - c, sep, ok := i.next() - if !ok { - return - } - if c == sep { - i.pushBack() - return - } - } -} - -func newIntMatcher(args []string) (patternMatcher, error) { - if len(args) != 1 { - return nil, fmt.Errorf("expected only one parameter - variable name, got: %s", args) - } - return &intMatcher{name: args[0]}, nil -} - -type intMatcher struct { - name string -} - -func (s *intMatcher) String() string { - return fmt.Sprintf("", s.name) -} - -func (s *intMatcher) getName() string { - return s.name -} - -func (s *intMatcher) match(iter *charIter) bool { - // count stores amount of consumed characters so we know how many push - // backs to do in case there is no match - var count int - - for { - c, sep, ok := iter.next() - count++ - - // if the current character is not a number: - // - it's either a separator that means it's a match - // - it's some other character that means it's not a match - if !unicode.IsDigit(rune(c)) { - if c == sep { - iter.pushBack() - return true - } else { - for i := 0; i < count; i++ { - iter.pushBack() - } - return false - } - } - - // if it's the end of the string, it's a match - if !ok { - return true - } - } -} - -func (s *intMatcher) equals(other patternMatcher) bool { - _, ok := other.(*intMatcher) - return ok && other.getName() == s.getName() -} - -func (e *trieNode) matchNode(i *charIter) bool { - if i.level() != e.level { - return false - } - if e.isRoot() { - return true - } - if e.isPatternMatcher() { - return e.patternMatcher.match(i) - } - c, _, ok := i.next() - if !ok { - // we have reached the end - return false - } - if c != e.char { - // no match, so don't consume the character - i.pushBack() - return false - } - return true -} - -func (e *trieNode) match(i *charIter) *match { - if !e.matchNode(i) { - return nil - } - // This is a leaf node and we are at the last character of the pattern - if len(e.matches) != 0 && i.isEnd() { - return e.matches[0] - } - // Check for the match in child nodes - for _, c := range e.children { - p := i.position() - if match := c.match(i); match != nil { - return match - } - i.setPosition(p) - } - // Child nodes did not match and we at the boundary - if len(e.matches) != 0 && i.level() > e.level { - return e.matches[0] - } - return nil -} - -// printTrie is useful for debugging and test purposes, it outputs the formatted -// represenation of the trie -func printTrie(t *trie) string { - return printTrieNode(t.root) -} - -func printTrieNode(e *trieNode) string { - out := &bytes.Buffer{} - printTrieNodeInner(out, e, 0) - return out.String() -} - -func printTrieNodeInner(b *bytes.Buffer, e *trieNode, offset int) { - if offset == 0 { - fmt.Fprintf(b, "\n") - } - padding := strings.Repeat(" ", offset) - fmt.Fprintf(b, "%s%s\n", padding, e.String()) - if len(e.children) != 0 { - for _, c := range e.children { - printTrieNodeInner(b, c, offset+1) - } - } -} diff --git a/vendor/github.com/vulcand/route/utils.go b/vendor/github.com/vulcand/route/utils.go deleted file mode 100644 index be186c8cf..000000000 --- a/vendor/github.com/vulcand/route/utils.go +++ /dev/null @@ -1,33 +0,0 @@ -package route - -import ( - "net/http" - "strings" -) - -// RawPath returns escaped url path section -func rawPath(r *http.Request) string { - // If there are no escape symbols, don't extract raw path - if !strings.ContainsRune(r.RequestURI, '%') { - if len(r.URL.Path) == 0 { - return "/" - } - return r.URL.Path - } - path := r.RequestURI - if path == "" { - path = "/" - } - // This is absolute URI, split host and port - if strings.Contains(path, "://") { - vals := strings.SplitN(path, r.URL.Host, 2) - if len(vals) == 2 { - path = vals[1] - } - } - idx := strings.IndexRune(path, '?') - if idx == -1 { - return path - } - return path[:idx] -} diff --git a/vendor/github.com/vulcand/vulcand/LICENSE b/vendor/github.com/vulcand/vulcand/LICENSE deleted file mode 100644 index ad410e113..000000000 --- a/vendor/github.com/vulcand/vulcand/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ -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. \ No newline at end of file diff --git a/vendor/github.com/vulcand/vulcand/conntracker/conntracker.go b/vendor/github.com/vulcand/vulcand/conntracker/conntracker.go deleted file mode 100644 index d37ddca0d..000000000 --- a/vendor/github.com/vulcand/vulcand/conntracker/conntracker.go +++ /dev/null @@ -1,13 +0,0 @@ -package conntracker - -import ( - "net" - "net/http" -) - -type ConnectionTracker interface { - RegisterStateChange(conn net.Conn, prev http.ConnState, cur http.ConnState) - Counts() ConnectionStats -} - -type ConnectionStats map[http.ConnState]map[string]int64 diff --git a/vendor/github.com/vulcand/vulcand/main.go b/vendor/github.com/vulcand/vulcand/main.go deleted file mode 100644 index bf77391ce..000000000 --- a/vendor/github.com/vulcand/vulcand/main.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "fmt" - "os" - "runtime" - - "github.com/vulcand/vulcand/plugin/registry" - "github.com/vulcand/vulcand/service" -) - -func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) - if err := service.Run(registry.GetRegistry()); err != nil { - fmt.Printf("Service exited with error: %s\n", err) - os.Exit(255) - } else { - fmt.Println("Service exited gracefully") - } -} diff --git a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/CacheProvider.go b/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/CacheProvider.go deleted file mode 100644 index 436b2d80e..000000000 --- a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/CacheProvider.go +++ /dev/null @@ -1,7 +0,0 @@ -package cacheprovider - -import "golang.org/x/crypto/acme/autocert" - -type T interface { - GetAutoCertCache() autocert.Cache -} diff --git a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/EtcdV2CacheProvider.go b/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/EtcdV2CacheProvider.go deleted file mode 100644 index 6b08cc929..000000000 --- a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/EtcdV2CacheProvider.go +++ /dev/null @@ -1,71 +0,0 @@ -package cacheprovider - -import ( - "context" - - etcd "github.com/coreos/etcd/client" - "golang.org/x/crypto/acme/autocert" -) - -func NewEtcdV2CacheProvider(kapi etcd.KeysAPI, vulcanPrefix string) T { - return &etcdv2CacheProvider{ - kapi: kapi, - vulcanPrefix: vulcanPrefix, - } -} - -type etcdv2CacheProvider struct { - kapi etcd.KeysAPI - vulcanPrefix string - autoCertCache autocert.Cache -} - -func (p *etcdv2CacheProvider) GetAutoCertCache() autocert.Cache { - if p.autoCertCache == nil { - p.autoCertCache = &etcdv2AutoCertCache{ - kapi: p.kapi, - prefix: p.vulcanPrefix + "/autocert_cache/", - } - } - return p.autoCertCache -} - -type etcdv2AutoCertCache struct { - kapi etcd.KeysAPI - prefix string -} - -// Get returns a certificate data for the specified key. -// If there's no such key, Get returns ErrCacheMiss. -func (ng *etcdv2AutoCertCache) Get(ctx context.Context, rawKey string) ([]byte, error) { - key := ng.normalized(rawKey) - r, err := ng.kapi.Get(ctx, key, &etcd.GetOptions{}) - if err != nil { - return nil, err - } - if r.Node == nil { - return nil, autocert.ErrCacheMiss - } - return []byte(r.Node.Value), nil -} - -// Put stores the data in the cache under the specified key. -// Inderlying implementations may use any data storage format, -// as long as the reverse operation, Get, results in the original data. -func (ng *etcdv2AutoCertCache) Put(ctx context.Context, rawKey string, data []byte) error { - key := ng.normalized(rawKey) - _, err := ng.kapi.Set(ctx, key, string(data), &etcd.SetOptions{}) - return err -} - -// Delete removes a certificate data from the cache under the specified key. -// If there's no such key in the cache, Delete returns nil. -func (ng *etcdv2AutoCertCache) Delete(ctx context.Context, rawKey string) error { - key := ng.normalized(rawKey) - _, err := ng.kapi.Delete(ctx, key, &etcd.DeleteOptions{}) - return err -} - -func (ng *etcdv2AutoCertCache) normalized(key string) string { - return ng.prefix + key -} diff --git a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/EtcdV3CacheProvider.go b/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/EtcdV3CacheProvider.go deleted file mode 100644 index bab5f5bc0..000000000 --- a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/EtcdV3CacheProvider.go +++ /dev/null @@ -1,76 +0,0 @@ -package cacheprovider - -import ( - "context" - - etcd "github.com/coreos/etcd/clientv3" - log "github.com/sirupsen/logrus" - "golang.org/x/crypto/acme/autocert" -) - -func NewEtcdV3CacheProvider(client *etcd.Client, vulcanPrefix string) T { - return &etcdv3CacheProvider{ - client: client, - vulcanPrefix: vulcanPrefix, - } -} - -type etcdv3CacheProvider struct { - client *etcd.Client - vulcanPrefix string - autoCertCache autocert.Cache -} - -func (p *etcdv3CacheProvider) GetAutoCertCache() autocert.Cache { - if p.autoCertCache == nil { - p.autoCertCache = &etcdv3AutoCertCache{ - client: p.client, - prefix: p.vulcanPrefix + "/autocert_cache/", - } - } - return p.autoCertCache -} - -type etcdv3AutoCertCache struct { - client *etcd.Client - prefix string -} - -// Get returns a certificate data for the specified key. -// If there's no such key, Get returns ErrCacheMiss. -func (ng *etcdv3AutoCertCache) Get(ctx context.Context, rawKey string) ([]byte, error) { - key := ng.normalize(rawKey) - r, err := ng.client.Get(ctx, key) - if err != nil { - return nil, err - } - if r.Count == 0 { - return nil, autocert.ErrCacheMiss - } - if r.Count > 1 { - log.Errorf("Against all odds, multiple results returned from Etcd when looking for single key: %s. "+ - "Returning the first one.", key) - } - return r.Kvs[0].Value, nil -} - -// Put stores the data in the cache under the specified key. -// Inderlying implementations may use any data storage format, -// as long as the reverse operation, Get, results in the original data. -func (ng *etcdv3AutoCertCache) Put(ctx context.Context, rawKey string, data []byte) error { - key := ng.normalize(rawKey) - _, err := ng.client.Put(ctx, key, string(data)) - return err -} - -// Delete removes a certificate data from the cache under the specified key. -// If there's no such key in the cache, Delete returns nil. -func (ng *etcdv3AutoCertCache) Delete(ctx context.Context, rawKey string) error { - key := ng.normalize(rawKey) - _, err := ng.client.Delete(ctx, key) - return err -} - -func (ng *etcdv3AutoCertCache) normalize(rawKey string) string { - return ng.prefix + rawKey -} diff --git a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/MemCacheProvider.go b/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/MemCacheProvider.go deleted file mode 100644 index 29da777b8..000000000 --- a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/MemCacheProvider.go +++ /dev/null @@ -1,61 +0,0 @@ -package cacheprovider - -import ( - "context" - "sync" - - "golang.org/x/crypto/acme/autocert" -) - -func NewMemCacheProvider() T { - return &memCacheProvider{} -} - -type memCacheProvider struct { - autocertCache autocert.Cache -} - -func (p *memCacheProvider) GetAutoCertCache() autocert.Cache { - if p.autocertCache == nil { - p.autocertCache = &memAutoCertCache{ - kv: make(map[string][]byte), - } - } - return p.autocertCache -} - -type memAutoCertCache struct { - kv map[string][]byte - mtx sync.Mutex -} - -// Get returns a certificate data for the specified key. -// If there's no such key, Get returns ErrCacheMiss. -func (ng *memAutoCertCache) Get(ctx context.Context, key string) ([]byte, error) { - ng.mtx.Lock() - defer ng.mtx.Unlock() - val, ok := ng.kv[key] - if ok { - return val, nil - } - return nil, autocert.ErrCacheMiss -} - -// Put stores the data in the cache under the specified key. -// Inderlying implementations may use any data storage format, -// as long as the reverse operation, Get, results in the original data. -func (ng *memAutoCertCache) Put(ctx context.Context, key string, data []byte) error { - ng.mtx.Lock() - defer ng.mtx.Unlock() - ng.kv[key] = data - return nil -} - -// Delete removes a certificate data from the cache under the specified key. -// If there's no such key in the cache, Delete returns nil. -func (ng *memAutoCertCache) Delete(ctx context.Context, key string) error { - ng.mtx.Lock() - defer ng.mtx.Unlock() - delete(ng.kv, key) - return nil -} diff --git a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/NoOpCacheProvider.go b/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/NoOpCacheProvider.go deleted file mode 100644 index 80805437e..000000000 --- a/vendor/github.com/vulcand/vulcand/plugin/cacheprovider/NoOpCacheProvider.go +++ /dev/null @@ -1,11 +0,0 @@ -package cacheprovider - -import "golang.org/x/crypto/acme/autocert" - -type noOpCacheProvider struct{} - -func (*noOpCacheProvider) GetAutoCertCache() autocert.Cache { return nil } - -func NoOp() T { - return &noOpCacheProvider{} -} diff --git a/vendor/github.com/vulcand/vulcand/plugin/middleware.go b/vendor/github.com/vulcand/vulcand/plugin/middleware.go deleted file mode 100644 index 54a6f1f51..000000000 --- a/vendor/github.com/vulcand/vulcand/plugin/middleware.go +++ /dev/null @@ -1,178 +0,0 @@ -package plugin - -import ( - "encoding/json" - "fmt" - "net/http" - "reflect" - - "github.com/codegangsta/cli" - "github.com/vulcand/oxy/forward" - "github.com/vulcand/oxy/roundrobin" - "github.com/vulcand/route" - "github.com/vulcand/vulcand/conntracker" - "github.com/vulcand/vulcand/plugin/cacheprovider" - "github.com/vulcand/vulcand/router" -) - -// Middleware specification, used to construct new middlewares and plug them into CLI API and backends -type MiddlewareSpec struct { - Type string - // Reader function that returns a middleware from another middleware structure - FromOther interface{} - // Flags for CLI tool to generate interface - CliFlags []cli.Flag - // Function that construtcs a middleware from CLI parameters - FromCli CliReader -} - -func (ms *MiddlewareSpec) FromJSON(data []byte) (Middleware, error) { - // Get a function's type - fnType := reflect.TypeOf(ms.FromOther) - - // Create a pointer to the function's first argument - ptr := reflect.New(fnType.In(0)).Interface() - err := json.Unmarshal(data, &ptr) - if err != nil { - return nil, fmt.Errorf("failed to decode %T from JSON, error: %s", ptr, err) - } - // Now let's call the function to produce a middleware - fnVal := reflect.ValueOf(ms.FromOther) - results := fnVal.Call([]reflect.Value{reflect.ValueOf(ptr).Elem()}) - - m, out := results[0].Interface(), results[1].Interface() - if out != nil { - return nil, out.(error) - } - return m.(Middleware), nil -} - -type Middleware interface { - NewHandler(http.Handler) (http.Handler, error) -} - -// Reader constructs the middleware from the CLI interface -type CliReader func(c *cli.Context) (Middleware, error) - -// Function that returns middleware spec by it's type -type SpecGetter func(string) *MiddlewareSpec - -// Holds a bunch of Listeners a frontend might have. -// This allows callers to consolidate all their listeners in one convenient struct. -type FrontendListeners struct { - ConnTck forward.UrlForwardingStateListener - RbRewriteListener roundrobin.RequestRewriteListener - RrRewriteListener roundrobin.RequestRewriteListener -} - -// Registry contains currently registered middlewares and used to support pluggable middlewares across all modules of the vulcand -type Registry struct { - specs []*MiddlewareSpec - notFound Middleware - router router.Router - incomingConnectionTracker conntracker.ConnectionTracker - frontendListeners FrontendListeners - cacheProvider cacheprovider.T -} - -func NewRegistry() *Registry { - return &Registry{ - specs: []*MiddlewareSpec{}, - router: route.NewMux(), - } -} - -func (r *Registry) AddSpec(s *MiddlewareSpec) error { - if s == nil { - return fmt.Errorf("spec can not be nil") - } - if r.GetSpec(s.Type) != nil { - return fmt.Errorf("middleware of type %s already registered", s.Type) - } - if err := verifySignature(s.FromOther); err != nil { - return err - } - r.specs = append(r.specs, s) - return nil -} - -func (r *Registry) GetSpec(middlewareType string) *MiddlewareSpec { - for _, s := range r.specs { - if s.Type == middlewareType { - return s - } - } - return nil -} - -func (r *Registry) GetSpecs() []*MiddlewareSpec { - return r.specs -} - -func (r *Registry) AddNotFoundMiddleware(notFound Middleware) error { - r.notFound = notFound - return nil -} - -func (r *Registry) GetNotFoundMiddleware() Middleware { - return r.notFound -} - -func (r *Registry) SetRouter(router router.Router) error { - r.router = router - return nil -} - -func (r *Registry) GetRouter() router.Router { - return r.router -} - -func (r *Registry) SetIncomingConnectionTracker(connTracker conntracker.ConnectionTracker) error { - r.incomingConnectionTracker = connTracker - return nil -} - -func (r *Registry) GetIncomingConnectionTracker() conntracker.ConnectionTracker { - return r.incomingConnectionTracker -} - -func (r *Registry) GetFrontendListeners() FrontendListeners { - return r.frontendListeners -} - -func (r *Registry) SetFrontendListeners(frontendListeners FrontendListeners) error { - r.frontendListeners = frontendListeners - return nil -} - -func (r *Registry) GetCacheProvider() cacheprovider.T { - return r.cacheProvider -} - -func (r *Registry) SetCacheProvider(cacheprovider cacheprovider.T) error { - r.cacheProvider = cacheprovider - return nil -} - -func verifySignature(fn interface{}) error { - t := reflect.TypeOf(fn) - if t == nil || t.Kind() != reflect.Func { - return fmt.Errorf("expected function, got %s", t) - } - if t.NumIn() != 1 { - return fmt.Errorf("expected function with one input argument, got %d", t.NumIn()) - } - if t.In(0).Kind() != reflect.Struct { - return fmt.Errorf("function argument should be struct, got %s", t.In(0).Kind()) - } - if t.NumOut() != 2 { - return fmt.Errorf("function should return 2 values, got %d", t.NumOut()) - } - if !t.Out(0).AssignableTo(reflect.TypeOf((*Middleware)(nil)).Elem()) { - return fmt.Errorf("function first return value should be Middleware got, %s", t.Out(0)) - } - if !t.Out(1).AssignableTo(reflect.TypeOf((*error)(nil)).Elem()) { - return fmt.Errorf("function second return value should be error got, %s", t.Out(1)) - } - return nil -} diff --git a/vendor/github.com/vulcand/vulcand/plugin/rewrite/rewrite.go b/vendor/github.com/vulcand/vulcand/plugin/rewrite/rewrite.go deleted file mode 100644 index b4c7ba2ba..000000000 --- a/vendor/github.com/vulcand/vulcand/plugin/rewrite/rewrite.go +++ /dev/null @@ -1,205 +0,0 @@ -package rewrite - -import ( - "bytes" - "fmt" - "io" - "net/http" - "net/url" - "regexp" - "strconv" - "strings" - - "github.com/codegangsta/cli" - log "github.com/sirupsen/logrus" - "github.com/vulcand/oxy/utils" - "github.com/vulcand/vulcand/plugin" -) - -const Type = "rewrite" - -type Rewrite struct { - Regexp string - Replacement string - RewriteBody bool - Redirect bool -} - -func NewRewrite(regex, replacement string, rewriteBody, redirect bool) (*Rewrite, error) { - return &Rewrite{regex, replacement, rewriteBody, redirect}, nil -} - -func (rw *Rewrite) NewHandler(next http.Handler) (http.Handler, error) { - return newRewriteHandler(next, rw) -} - -func (rw *Rewrite) String() string { - return fmt.Sprintf("regexp=%v, replacement=%v, rewriteBody=%v, redirect=%v", - rw.Regexp, rw.Replacement, rw.RewriteBody, rw.Redirect) -} - -type rewriteHandler struct { - next http.Handler - errHandler utils.ErrorHandler - regexp *regexp.Regexp - replacement string - rewriteBody bool - redirect bool -} - -func newRewriteHandler(next http.Handler, spec *Rewrite) (*rewriteHandler, error) { - re, err := regexp.Compile(spec.Regexp) - if err != nil { - return nil, err - } - return &rewriteHandler{ - regexp: re, - replacement: spec.Replacement, - rewriteBody: spec.RewriteBody, - redirect: spec.Redirect, - next: next, - errHandler: utils.DefaultHandler, - }, nil -} - -func (rw *rewriteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - oldURL := rawURL(req) - - // only continue if the Regexp param matches the URL - if !rw.regexp.MatchString(oldURL) { - rw.next.ServeHTTP(w, req) - return - } - - // apply a rewrite regexp to the URL - newURL := rw.regexp.ReplaceAllString(oldURL, rw.replacement) - - // replace any variables that may be in there - rewrittenURL := &bytes.Buffer{} - if err := ApplyString(newURL, rewrittenURL, req); err != nil { - rw.errHandler.ServeHTTP(w, req, err) - return - } - - // parse the rewritten URL and replace request URL with it - parsedURL, err := url.Parse(rewrittenURL.String()) - if err != nil { - rw.errHandler.ServeHTTP(w, req, err) - return - } - - if rw.redirect && newURL != oldURL { - (&redirectHandler{u: parsedURL}).ServeHTTP(w, req) - return - } - - req.URL = parsedURL - - // make sure the request URI corresponds the rewritten URL - req.RequestURI = req.URL.RequestURI() - - if !rw.rewriteBody { - rw.next.ServeHTTP(w, req) - return - } - - bw := &bufferWriter{header: make(http.Header), buffer: &bytes.Buffer{}} - newBody := &bytes.Buffer{} - - rw.next.ServeHTTP(bw, req) - - if err := Apply(bw.buffer, newBody, req); err != nil { - log.Errorf("While rewriting response body for '%s': %v", req.RequestURI, err) - } - - utils.CopyHeaders(w.Header(), bw.Header()) - w.Header().Set("Content-Length", strconv.Itoa(newBody.Len())) - w.WriteHeader(bw.code) - io.Copy(w, newBody) -} - -func FromOther(rw Rewrite) (plugin.Middleware, error) { - return NewRewrite(rw.Regexp, rw.Replacement, rw.RewriteBody, rw.Redirect) -} - -func FromCli(c *cli.Context) (plugin.Middleware, error) { - return NewRewrite(c.String("regexp"), c.String("replacement"), c.Bool("rewriteBody"), c.Bool("redirect")) -} - -func GetSpec() *plugin.MiddlewareSpec { - return &plugin.MiddlewareSpec{ - Type: Type, - FromOther: FromOther, - FromCli: FromCli, - CliFlags: CliFlags(), - } -} - -func CliFlags() []cli.Flag { - return []cli.Flag{ - cli.StringFlag{ - Name: "regexp", - Usage: "regex to match against http request path", - }, - cli.StringFlag{ - Name: "replacement", - Usage: "replacement text into which regex expansions are inserted", - }, - cli.BoolFlag{ - Name: "rewriteBody", - Usage: "if provided, response body is treated as as template and all variables in it are replaced", - }, - cli.BoolFlag{ - Name: "redirect", - Usage: "if provided, request is redirected to the rewritten URL", - }, - } -} - -func rawURL(request *http.Request) string { - scheme := "http" - if request.TLS != nil || isXForwardedHTTPS(request) { - scheme = "https" - } - - return strings.Join([]string{scheme, "://", request.Host, request.RequestURI}, "") -} - -func isXForwardedHTTPS(request *http.Request) bool { - xForwardedProto := request.Header.Get("X-Forwarded-Proto") - - return len(xForwardedProto) > 0 && xForwardedProto == "https" -} - -type redirectHandler struct { - u *url.URL -} - -func (f *redirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Location", f.u.String()) - w.WriteHeader(http.StatusFound) - w.Write([]byte(http.StatusText(http.StatusFound))) -} - -type bufferWriter struct { - header http.Header - code int - buffer *bytes.Buffer -} - -func (b *bufferWriter) Close() error { - return nil -} - -func (b *bufferWriter) Header() http.Header { - return b.header -} - -func (b *bufferWriter) Write(buf []byte) (int, error) { - return b.buffer.Write(buf) -} - -// WriteHeader sets rw.Code. -func (b *bufferWriter) WriteHeader(code int) { - b.code = code -} diff --git a/vendor/github.com/vulcand/vulcand/plugin/rewrite/template.go b/vendor/github.com/vulcand/vulcand/plugin/rewrite/template.go deleted file mode 100644 index a0a5aab7b..000000000 --- a/vendor/github.com/vulcand/vulcand/plugin/rewrite/template.go +++ /dev/null @@ -1,44 +0,0 @@ -package rewrite - -import ( - "io" - "io/ioutil" - "net/http" - "text/template" -) - -// data represents template data that is available to use in templates. -type data struct { - Request *http.Request -} - -// Apply reads a template string from the provided reader, applies variables -// from the provided request object to it and writes the result into -// the provided writer. -// -// Template is standard Go's http://golang.org/pkg/text/template/. -func Apply(in io.Reader, out io.Writer, request *http.Request) error { - body, err := ioutil.ReadAll(in) - if err != nil { - return err - } - - return ApplyString(string(body), out, request) -} - -// ApplyString applies variables from the provided request object to the provided -// template string and writes the result into the provided writer. -// -// Template is standard Go's http://golang.org/pkg/text/template/. -func ApplyString(in string, out io.Writer, request *http.Request) error { - t, err := template.New("t").Parse(in) - if err != nil { - return err - } - - if err = t.Execute(out, data{request}); err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/vulcand/vulcand/router/router.go b/vendor/github.com/vulcand/vulcand/router/router.go deleted file mode 100644 index 93d194f57..000000000 --- a/vendor/github.com/vulcand/vulcand/router/router.go +++ /dev/null @@ -1,26 +0,0 @@ -package router - -import "net/http" - -// This interface captures all routing functionality required by vulcan. -// The routing functionality mainly comes from "github.com/vulcand/route", -type Router interface { - - // Sets the not-found handler (this handler is called when no other handlers/routes in the routing library match - SetNotFound(http.Handler) error - - // Gets the not-found handler that is currently in use by this router. - GetNotFound() http.Handler - - // Validates whether this is an acceptable route expression - IsValid(string) bool - - // Adds a new route->handler combination. The route is a string which provides the routing expression. http.Handler is called when this expression matches a request. - Handle(string, http.Handler) error - - // Removes a route. The http.Handler associated with it, will be discarded. - Remove(string) error - - // ServiceHTTP is the http.Handler implementation that allows callers to route their calls to sub-http.Handlers based on route matches. - ServeHTTP(http.ResponseWriter, *http.Request) -} diff --git a/vendor/golang.org/x/crypto/acme/acme.go b/vendor/golang.org/x/crypto/acme/acme.go deleted file mode 100644 index fa9c4b39e..000000000 --- a/vendor/golang.org/x/crypto/acme/acme.go +++ /dev/null @@ -1,1058 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package acme provides an implementation of the -// Automatic Certificate Management Environment (ACME) spec. -// See https://tools.ietf.org/html/draft-ietf-acme-acme-02 for details. -// -// Most common scenarios will want to use autocert subdirectory instead, -// which provides automatic access to certificates from Let's Encrypt -// and any other ACME-based CA. -// -// This package is a work in progress and makes no API stability promises. -package acme - -import ( - "bytes" - "context" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha256" - "crypto/tls" - "crypto/x509" - "encoding/base64" - "encoding/hex" - "encoding/json" - "encoding/pem" - "errors" - "fmt" - "io" - "io/ioutil" - "math/big" - "net/http" - "strconv" - "strings" - "sync" - "time" -) - -// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA. -const LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory" - -const ( - maxChainLen = 5 // max depth and breadth of a certificate chain - maxCertSize = 1 << 20 // max size of a certificate, in bytes - - // Max number of collected nonces kept in memory. - // Expect usual peak of 1 or 2. - maxNonces = 100 -) - -// Client is an ACME client. -// The only required field is Key. An example of creating a client with a new key -// is as follows: -// -// key, err := rsa.GenerateKey(rand.Reader, 2048) -// if err != nil { -// log.Fatal(err) -// } -// client := &Client{Key: key} -// -type Client struct { - // Key is the account key used to register with a CA and sign requests. - // Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey. - Key crypto.Signer - - // HTTPClient optionally specifies an HTTP client to use - // instead of http.DefaultClient. - HTTPClient *http.Client - - // DirectoryURL points to the CA directory endpoint. - // If empty, LetsEncryptURL is used. - // Mutating this value after a successful call of Client's Discover method - // will have no effect. - DirectoryURL string - - dirMu sync.Mutex // guards writes to dir - dir *Directory // cached result of Client's Discover method - - noncesMu sync.Mutex - nonces map[string]struct{} // nonces collected from previous responses -} - -// Discover performs ACME server discovery using c.DirectoryURL. -// -// It caches successful result. So, subsequent calls will not result in -// a network round-trip. This also means mutating c.DirectoryURL after successful call -// of this method will have no effect. -func (c *Client) Discover(ctx context.Context) (Directory, error) { - c.dirMu.Lock() - defer c.dirMu.Unlock() - if c.dir != nil { - return *c.dir, nil - } - - dirURL := c.DirectoryURL - if dirURL == "" { - dirURL = LetsEncryptURL - } - res, err := c.get(ctx, dirURL) - if err != nil { - return Directory{}, err - } - defer res.Body.Close() - c.addNonce(res.Header) - if res.StatusCode != http.StatusOK { - return Directory{}, responseError(res) - } - - var v struct { - Reg string `json:"new-reg"` - Authz string `json:"new-authz"` - Cert string `json:"new-cert"` - Revoke string `json:"revoke-cert"` - Meta struct { - Terms string `json:"terms-of-service"` - Website string `json:"website"` - CAA []string `json:"caa-identities"` - } - } - if err := json.NewDecoder(res.Body).Decode(&v); err != nil { - return Directory{}, err - } - c.dir = &Directory{ - RegURL: v.Reg, - AuthzURL: v.Authz, - CertURL: v.Cert, - RevokeURL: v.Revoke, - Terms: v.Meta.Terms, - Website: v.Meta.Website, - CAA: v.Meta.CAA, - } - return *c.dir, nil -} - -// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format. -// The exp argument indicates the desired certificate validity duration. CA may issue a certificate -// with a different duration. -// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain. -// -// In the case where CA server does not provide the issued certificate in the response, -// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips. -// In such a scenario, the caller can cancel the polling with ctx. -// -// CreateCert returns an error if the CA's response or chain was unreasonably large. -// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features. -func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) { - if _, err := c.Discover(ctx); err != nil { - return nil, "", err - } - - req := struct { - Resource string `json:"resource"` - CSR string `json:"csr"` - NotBefore string `json:"notBefore,omitempty"` - NotAfter string `json:"notAfter,omitempty"` - }{ - Resource: "new-cert", - CSR: base64.RawURLEncoding.EncodeToString(csr), - } - now := timeNow() - req.NotBefore = now.Format(time.RFC3339) - if exp > 0 { - req.NotAfter = now.Add(exp).Format(time.RFC3339) - } - - res, err := c.retryPostJWS(ctx, c.Key, c.dir.CertURL, req) - if err != nil { - return nil, "", err - } - defer res.Body.Close() - if res.StatusCode != http.StatusCreated { - return nil, "", responseError(res) - } - - curl := res.Header.Get("Location") // cert permanent URL - if res.ContentLength == 0 { - // no cert in the body; poll until we get it - cert, err := c.FetchCert(ctx, curl, bundle) - return cert, curl, err - } - // slurp issued cert and CA chain, if requested - cert, err := c.responseCert(ctx, res, bundle) - return cert, curl, err -} - -// FetchCert retrieves already issued certificate from the given url, in DER format. -// It retries the request until the certificate is successfully retrieved, -// context is cancelled by the caller or an error response is received. -// -// The returned value will also contain the CA (issuer) certificate if the bundle argument is true. -// -// FetchCert returns an error if the CA's response or chain was unreasonably large. -// Callers are encouraged to parse the returned value to ensure the certificate is valid -// and has expected features. -func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) { - for { - res, err := c.get(ctx, url) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode == http.StatusOK { - return c.responseCert(ctx, res, bundle) - } - if res.StatusCode > 299 { - return nil, responseError(res) - } - d := retryAfter(res.Header.Get("Retry-After"), 3*time.Second) - select { - case <-time.After(d): - // retry - case <-ctx.Done(): - return nil, ctx.Err() - } - } -} - -// RevokeCert revokes a previously issued certificate cert, provided in DER format. -// -// The key argument, used to sign the request, must be authorized -// to revoke the certificate. It's up to the CA to decide which keys are authorized. -// For instance, the key pair of the certificate may be authorized. -// If the key is nil, c.Key is used instead. -func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error { - if _, err := c.Discover(ctx); err != nil { - return err - } - - body := &struct { - Resource string `json:"resource"` - Cert string `json:"certificate"` - Reason int `json:"reason"` - }{ - Resource: "revoke-cert", - Cert: base64.RawURLEncoding.EncodeToString(cert), - Reason: int(reason), - } - if key == nil { - key = c.Key - } - res, err := c.retryPostJWS(ctx, key, c.dir.RevokeURL, body) - if err != nil { - return err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return responseError(res) - } - return nil -} - -// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service -// during account registration. See Register method of Client for more details. -func AcceptTOS(tosURL string) bool { return true } - -// Register creates a new account registration by following the "new-reg" flow. -// It returns the registered account. The account is not modified. -// -// The registration may require the caller to agree to the CA's Terms of Service (TOS). -// If so, and the account has not indicated the acceptance of the terms (see Account for details), -// Register calls prompt with a TOS URL provided by the CA. Prompt should report -// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS. -func (c *Client) Register(ctx context.Context, a *Account, prompt func(tosURL string) bool) (*Account, error) { - if _, err := c.Discover(ctx); err != nil { - return nil, err - } - - var err error - if a, err = c.doReg(ctx, c.dir.RegURL, "new-reg", a); err != nil { - return nil, err - } - var accept bool - if a.CurrentTerms != "" && a.CurrentTerms != a.AgreedTerms { - accept = prompt(a.CurrentTerms) - } - if accept { - a.AgreedTerms = a.CurrentTerms - a, err = c.UpdateReg(ctx, a) - } - return a, err -} - -// GetReg retrieves an existing registration. -// The url argument is an Account URI. -func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) { - a, err := c.doReg(ctx, url, "reg", nil) - if err != nil { - return nil, err - } - a.URI = url - return a, nil -} - -// UpdateReg updates an existing registration. -// It returns an updated account copy. The provided account is not modified. -func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) { - uri := a.URI - a, err := c.doReg(ctx, uri, "reg", a) - if err != nil { - return nil, err - } - a.URI = uri - return a, nil -} - -// Authorize performs the initial step in an authorization flow. -// The caller will then need to choose from and perform a set of returned -// challenges using c.Accept in order to successfully complete authorization. -// -// If an authorization has been previously granted, the CA may return -// a valid authorization (Authorization.Status is StatusValid). If so, the caller -// need not fulfill any challenge and can proceed to requesting a certificate. -func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) { - if _, err := c.Discover(ctx); err != nil { - return nil, err - } - - type authzID struct { - Type string `json:"type"` - Value string `json:"value"` - } - req := struct { - Resource string `json:"resource"` - Identifier authzID `json:"identifier"` - }{ - Resource: "new-authz", - Identifier: authzID{Type: "dns", Value: domain}, - } - res, err := c.retryPostJWS(ctx, c.Key, c.dir.AuthzURL, req) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode != http.StatusCreated { - return nil, responseError(res) - } - - var v wireAuthz - if err := json.NewDecoder(res.Body).Decode(&v); err != nil { - return nil, fmt.Errorf("acme: invalid response: %v", err) - } - if v.Status != StatusPending && v.Status != StatusValid { - return nil, fmt.Errorf("acme: unexpected status: %s", v.Status) - } - return v.authorization(res.Header.Get("Location")), nil -} - -// GetAuthorization retrieves an authorization identified by the given URL. -// -// If a caller needs to poll an authorization until its status is final, -// see the WaitAuthorization method. -func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) { - res, err := c.get(ctx, url) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { - return nil, responseError(res) - } - var v wireAuthz - if err := json.NewDecoder(res.Body).Decode(&v); err != nil { - return nil, fmt.Errorf("acme: invalid response: %v", err) - } - return v.authorization(url), nil -} - -// RevokeAuthorization relinquishes an existing authorization identified -// by the given URL. -// The url argument is an Authorization.URI value. -// -// If successful, the caller will be required to obtain a new authorization -// using the Authorize method before being able to request a new certificate -// for the domain associated with the authorization. -// -// It does not revoke existing certificates. -func (c *Client) RevokeAuthorization(ctx context.Context, url string) error { - req := struct { - Resource string `json:"resource"` - Status string `json:"status"` - Delete bool `json:"delete"` - }{ - Resource: "authz", - Status: "deactivated", - Delete: true, - } - res, err := c.retryPostJWS(ctx, c.Key, url, req) - if err != nil { - return err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return responseError(res) - } - return nil -} - -// WaitAuthorization polls an authorization at the given URL -// until it is in one of the final states, StatusValid or StatusInvalid, -// or the context is done. -// -// It returns a non-nil Authorization only if its Status is StatusValid. -// In all other cases WaitAuthorization returns an error. -// If the Status is StatusInvalid, the returned error is of type *AuthorizationError. -func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) { - sleep := sleeper(ctx) - for { - res, err := c.get(ctx, url) - if err != nil { - return nil, err - } - retry := res.Header.Get("Retry-After") - if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { - res.Body.Close() - if err := sleep(retry, 1); err != nil { - return nil, err - } - continue - } - var raw wireAuthz - err = json.NewDecoder(res.Body).Decode(&raw) - res.Body.Close() - if err != nil { - if err := sleep(retry, 0); err != nil { - return nil, err - } - continue - } - if raw.Status == StatusValid { - return raw.authorization(url), nil - } - if raw.Status == StatusInvalid { - return nil, raw.error(url) - } - if err := sleep(retry, 0); err != nil { - return nil, err - } - } -} - -// GetChallenge retrieves the current status of an challenge. -// -// A client typically polls a challenge status using this method. -func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) { - res, err := c.get(ctx, url) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { - return nil, responseError(res) - } - v := wireChallenge{URI: url} - if err := json.NewDecoder(res.Body).Decode(&v); err != nil { - return nil, fmt.Errorf("acme: invalid response: %v", err) - } - return v.challenge(), nil -} - -// Accept informs the server that the client accepts one of its challenges -// previously obtained with c.Authorize. -// -// The server will then perform the validation asynchronously. -func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) { - auth, err := keyAuth(c.Key.Public(), chal.Token) - if err != nil { - return nil, err - } - - req := struct { - Resource string `json:"resource"` - Type string `json:"type"` - Auth string `json:"keyAuthorization"` - }{ - Resource: "challenge", - Type: chal.Type, - Auth: auth, - } - res, err := c.retryPostJWS(ctx, c.Key, chal.URI, req) - if err != nil { - return nil, err - } - defer res.Body.Close() - // Note: the protocol specifies 200 as the expected response code, but - // letsencrypt seems to be returning 202. - if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { - return nil, responseError(res) - } - - var v wireChallenge - if err := json.NewDecoder(res.Body).Decode(&v); err != nil { - return nil, fmt.Errorf("acme: invalid response: %v", err) - } - return v.challenge(), nil -} - -// DNS01ChallengeRecord returns a DNS record value for a dns-01 challenge response. -// A TXT record containing the returned value must be provisioned under -// "_acme-challenge" name of the domain being validated. -// -// The token argument is a Challenge.Token value. -func (c *Client) DNS01ChallengeRecord(token string) (string, error) { - ka, err := keyAuth(c.Key.Public(), token) - if err != nil { - return "", err - } - b := sha256.Sum256([]byte(ka)) - return base64.RawURLEncoding.EncodeToString(b[:]), nil -} - -// HTTP01ChallengeResponse returns the response for an http-01 challenge. -// Servers should respond with the value to HTTP requests at the URL path -// provided by HTTP01ChallengePath to validate the challenge and prove control -// over a domain name. -// -// The token argument is a Challenge.Token value. -func (c *Client) HTTP01ChallengeResponse(token string) (string, error) { - return keyAuth(c.Key.Public(), token) -} - -// HTTP01ChallengePath returns the URL path at which the response for an http-01 challenge -// should be provided by the servers. -// The response value can be obtained with HTTP01ChallengeResponse. -// -// The token argument is a Challenge.Token value. -func (c *Client) HTTP01ChallengePath(token string) string { - return "/.well-known/acme-challenge/" + token -} - -// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response. -// Servers can present the certificate to validate the challenge and prove control -// over a domain name. -// -// The implementation is incomplete in that the returned value is a single certificate, -// computed only for Z0 of the key authorization. ACME CAs are expected to update -// their implementations to use the newer version, TLS-SNI-02. -// For more details on TLS-SNI-01 see https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3. -// -// The token argument is a Challenge.Token value. -// If a WithKey option is provided, its private part signs the returned cert, -// and the public part is used to specify the signee. -// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve. -// -// The returned certificate is valid for the next 24 hours and must be presented only when -// the server name of the client hello matches exactly the returned name value. -func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) { - ka, err := keyAuth(c.Key.Public(), token) - if err != nil { - return tls.Certificate{}, "", err - } - b := sha256.Sum256([]byte(ka)) - h := hex.EncodeToString(b[:]) - name = fmt.Sprintf("%s.%s.acme.invalid", h[:32], h[32:]) - cert, err = tlsChallengeCert([]string{name}, opt) - if err != nil { - return tls.Certificate{}, "", err - } - return cert, name, nil -} - -// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response. -// Servers can present the certificate to validate the challenge and prove control -// over a domain name. For more details on TLS-SNI-02 see -// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3. -// -// The token argument is a Challenge.Token value. -// If a WithKey option is provided, its private part signs the returned cert, -// and the public part is used to specify the signee. -// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve. -// -// The returned certificate is valid for the next 24 hours and must be presented only when -// the server name in the client hello matches exactly the returned name value. -func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) { - b := sha256.Sum256([]byte(token)) - h := hex.EncodeToString(b[:]) - sanA := fmt.Sprintf("%s.%s.token.acme.invalid", h[:32], h[32:]) - - ka, err := keyAuth(c.Key.Public(), token) - if err != nil { - return tls.Certificate{}, "", err - } - b = sha256.Sum256([]byte(ka)) - h = hex.EncodeToString(b[:]) - sanB := fmt.Sprintf("%s.%s.ka.acme.invalid", h[:32], h[32:]) - - cert, err = tlsChallengeCert([]string{sanA, sanB}, opt) - if err != nil { - return tls.Certificate{}, "", err - } - return cert, sanA, nil -} - -// doReg sends all types of registration requests. -// The type of request is identified by typ argument, which is a "resource" -// in the ACME spec terms. -// -// A non-nil acct argument indicates whether the intention is to mutate data -// of the Account. Only Contact and Agreement of its fields are used -// in such cases. -func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Account) (*Account, error) { - req := struct { - Resource string `json:"resource"` - Contact []string `json:"contact,omitempty"` - Agreement string `json:"agreement,omitempty"` - }{ - Resource: typ, - } - if acct != nil { - req.Contact = acct.Contact - req.Agreement = acct.AgreedTerms - } - res, err := c.retryPostJWS(ctx, c.Key, url, req) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode < 200 || res.StatusCode > 299 { - return nil, responseError(res) - } - - var v struct { - Contact []string - Agreement string - Authorizations string - Certificates string - } - if err := json.NewDecoder(res.Body).Decode(&v); err != nil { - return nil, fmt.Errorf("acme: invalid response: %v", err) - } - var tos string - if v := linkHeader(res.Header, "terms-of-service"); len(v) > 0 { - tos = v[0] - } - var authz string - if v := linkHeader(res.Header, "next"); len(v) > 0 { - authz = v[0] - } - return &Account{ - URI: res.Header.Get("Location"), - Contact: v.Contact, - AgreedTerms: v.Agreement, - CurrentTerms: tos, - Authz: authz, - Authorizations: v.Authorizations, - Certificates: v.Certificates, - }, nil -} - -// retryPostJWS will retry calls to postJWS if there is a badNonce error, -// clearing the stored nonces after each error. -// If the response was 4XX-5XX, then responseError is called on the body, -// the body is closed, and the error returned. -func (c *Client) retryPostJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) { - sleep := sleeper(ctx) - for { - res, err := c.postJWS(ctx, key, url, body) - if err != nil { - return nil, err - } - // handle errors 4XX-5XX with responseError - if res.StatusCode >= 400 && res.StatusCode <= 599 { - err := responseError(res) - res.Body.Close() - // according to spec badNonce is urn:ietf:params:acme:error:badNonce - // however, acme servers in the wild return their version of the error - // https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4 - if ae, ok := err.(*Error); ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce") { - // clear any nonces that we might've stored that might now be - // considered bad - c.clearNonces() - retry := res.Header.Get("Retry-After") - if err := sleep(retry, 1); err != nil { - return nil, err - } - continue - } - return nil, err - } - return res, nil - } -} - -// postJWS signs the body with the given key and POSTs it to the provided url. -// The body argument must be JSON-serializable. -func (c *Client) postJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) { - nonce, err := c.popNonce(ctx, url) - if err != nil { - return nil, err - } - b, err := jwsEncodeJSON(body, key, nonce) - if err != nil { - return nil, err - } - res, err := c.post(ctx, url, "application/jose+json", bytes.NewReader(b)) - if err != nil { - return nil, err - } - c.addNonce(res.Header) - return res, nil -} - -// popNonce returns a nonce value previously stored with c.addNonce -// or fetches a fresh one from the given URL. -func (c *Client) popNonce(ctx context.Context, url string) (string, error) { - c.noncesMu.Lock() - defer c.noncesMu.Unlock() - if len(c.nonces) == 0 { - return c.fetchNonce(ctx, url) - } - var nonce string - for nonce = range c.nonces { - delete(c.nonces, nonce) - break - } - return nonce, nil -} - -// clearNonces clears any stored nonces -func (c *Client) clearNonces() { - c.noncesMu.Lock() - defer c.noncesMu.Unlock() - c.nonces = make(map[string]struct{}) -} - -// addNonce stores a nonce value found in h (if any) for future use. -func (c *Client) addNonce(h http.Header) { - v := nonceFromHeader(h) - if v == "" { - return - } - c.noncesMu.Lock() - defer c.noncesMu.Unlock() - if len(c.nonces) >= maxNonces { - return - } - if c.nonces == nil { - c.nonces = make(map[string]struct{}) - } - c.nonces[v] = struct{}{} -} - -func (c *Client) httpClient() *http.Client { - if c.HTTPClient != nil { - return c.HTTPClient - } - return http.DefaultClient -} - -func (c *Client) get(ctx context.Context, urlStr string) (*http.Response, error) { - req, err := http.NewRequest("GET", urlStr, nil) - if err != nil { - return nil, err - } - return c.do(ctx, req) -} - -func (c *Client) head(ctx context.Context, urlStr string) (*http.Response, error) { - req, err := http.NewRequest("HEAD", urlStr, nil) - if err != nil { - return nil, err - } - return c.do(ctx, req) -} - -func (c *Client) post(ctx context.Context, urlStr, contentType string, body io.Reader) (*http.Response, error) { - req, err := http.NewRequest("POST", urlStr, body) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", contentType) - return c.do(ctx, req) -} - -func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, error) { - res, err := c.httpClient().Do(req.WithContext(ctx)) - if err != nil { - select { - case <-ctx.Done(): - // Prefer the unadorned context error. - // (The acme package had tests assuming this, previously from ctxhttp's - // behavior, predating net/http supporting contexts natively) - // TODO(bradfitz): reconsider this in the future. But for now this - // requires no test updates. - return nil, ctx.Err() - default: - return nil, err - } - } - return res, nil -} - -func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) { - resp, err := c.head(ctx, url) - if err != nil { - return "", err - } - defer resp.Body.Close() - nonce := nonceFromHeader(resp.Header) - if nonce == "" { - if resp.StatusCode > 299 { - return "", responseError(resp) - } - return "", errors.New("acme: nonce not found") - } - return nonce, nil -} - -func nonceFromHeader(h http.Header) string { - return h.Get("Replay-Nonce") -} - -func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bool) ([][]byte, error) { - b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1)) - if err != nil { - return nil, fmt.Errorf("acme: response stream: %v", err) - } - if len(b) > maxCertSize { - return nil, errors.New("acme: certificate is too big") - } - cert := [][]byte{b} - if !bundle { - return cert, nil - } - - // Append CA chain cert(s). - // At least one is required according to the spec: - // https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-6.3.1 - up := linkHeader(res.Header, "up") - if len(up) == 0 { - return nil, errors.New("acme: rel=up link not found") - } - if len(up) > maxChainLen { - return nil, errors.New("acme: rel=up link is too large") - } - for _, url := range up { - cc, err := c.chainCert(ctx, url, 0) - if err != nil { - return nil, err - } - cert = append(cert, cc...) - } - return cert, nil -} - -// responseError creates an error of Error type from resp. -func responseError(resp *http.Response) error { - // don't care if ReadAll returns an error: - // json.Unmarshal will fail in that case anyway - b, _ := ioutil.ReadAll(resp.Body) - e := &wireError{Status: resp.StatusCode} - if err := json.Unmarshal(b, e); err != nil { - // this is not a regular error response: - // populate detail with anything we received, - // e.Status will already contain HTTP response code value - e.Detail = string(b) - if e.Detail == "" { - e.Detail = resp.Status - } - } - return e.error(resp.Header) -} - -// chainCert fetches CA certificate chain recursively by following "up" links. -// Each recursive call increments the depth by 1, resulting in an error -// if the recursion level reaches maxChainLen. -// -// First chainCert call starts with depth of 0. -func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte, error) { - if depth >= maxChainLen { - return nil, errors.New("acme: certificate chain is too deep") - } - - res, err := c.get(ctx, url) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return nil, responseError(res) - } - b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1)) - if err != nil { - return nil, err - } - if len(b) > maxCertSize { - return nil, errors.New("acme: certificate is too big") - } - chain := [][]byte{b} - - uplink := linkHeader(res.Header, "up") - if len(uplink) > maxChainLen { - return nil, errors.New("acme: certificate chain is too large") - } - for _, up := range uplink { - cc, err := c.chainCert(ctx, up, depth+1) - if err != nil { - return nil, err - } - chain = append(chain, cc...) - } - - return chain, nil -} - -// linkHeader returns URI-Reference values of all Link headers -// with relation-type rel. -// See https://tools.ietf.org/html/rfc5988#section-5 for details. -func linkHeader(h http.Header, rel string) []string { - var links []string - for _, v := range h["Link"] { - parts := strings.Split(v, ";") - for _, p := range parts { - p = strings.TrimSpace(p) - if !strings.HasPrefix(p, "rel=") { - continue - } - if v := strings.Trim(p[4:], `"`); v == rel { - links = append(links, strings.Trim(parts[0], "<>")) - } - } - } - return links -} - -// sleeper returns a function that accepts the Retry-After HTTP header value -// and an increment that's used with backoff to increasingly sleep on -// consecutive calls until the context is done. If the Retry-After header -// cannot be parsed, then backoff is used with a maximum sleep time of 10 -// seconds. -func sleeper(ctx context.Context) func(ra string, inc int) error { - var count int - return func(ra string, inc int) error { - count += inc - d := backoff(count, 10*time.Second) - d = retryAfter(ra, d) - wakeup := time.NewTimer(d) - defer wakeup.Stop() - select { - case <-ctx.Done(): - return ctx.Err() - case <-wakeup.C: - return nil - } - } -} - -// retryAfter parses a Retry-After HTTP header value, -// trying to convert v into an int (seconds) or use http.ParseTime otherwise. -// It returns d if v cannot be parsed. -func retryAfter(v string, d time.Duration) time.Duration { - if i, err := strconv.Atoi(v); err == nil { - return time.Duration(i) * time.Second - } - t, err := http.ParseTime(v) - if err != nil { - return d - } - return t.Sub(timeNow()) -} - -// backoff computes a duration after which an n+1 retry iteration should occur -// using truncated exponential backoff algorithm. -// -// The n argument is always bounded between 0 and 30. -// The max argument defines upper bound for the returned value. -func backoff(n int, max time.Duration) time.Duration { - if n < 0 { - n = 0 - } - if n > 30 { - n = 30 - } - var d time.Duration - if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil { - d = time.Duration(x.Int64()) * time.Millisecond - } - d += time.Duration(1< max { - return max - } - return d -} - -// keyAuth generates a key authorization string for a given token. -func keyAuth(pub crypto.PublicKey, token string) (string, error) { - th, err := JWKThumbprint(pub) - if err != nil { - return "", err - } - return fmt.Sprintf("%s.%s", token, th), nil -} - -// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges -// with the given SANs and auto-generated public/private key pair. -// The Subject Common Name is set to the first SAN to aid debugging. -// To create a cert with a custom key pair, specify WithKey option. -func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) { - var ( - key crypto.Signer - tmpl *x509.Certificate - ) - for _, o := range opt { - switch o := o.(type) { - case *certOptKey: - if key != nil { - return tls.Certificate{}, errors.New("acme: duplicate key option") - } - key = o.key - case *certOptTemplate: - var t = *(*x509.Certificate)(o) // shallow copy is ok - tmpl = &t - default: - // package's fault, if we let this happen: - panic(fmt.Sprintf("unsupported option type %T", o)) - } - } - if key == nil { - var err error - if key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil { - return tls.Certificate{}, err - } - } - if tmpl == nil { - tmpl = &x509.Certificate{ - SerialNumber: big.NewInt(1), - NotBefore: time.Now(), - NotAfter: time.Now().Add(24 * time.Hour), - BasicConstraintsValid: true, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - } - } - tmpl.DNSNames = san - if len(san) > 0 { - tmpl.Subject.CommonName = san[0] - } - - der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key) - if err != nil { - return tls.Certificate{}, err - } - return tls.Certificate{ - Certificate: [][]byte{der}, - PrivateKey: key, - }, nil -} - -// encodePEM returns b encoded as PEM with block of type typ. -func encodePEM(typ string, b []byte) []byte { - pb := &pem.Block{Type: typ, Bytes: b} - return pem.EncodeToMemory(pb) -} - -// timeNow is useful for testing for fixed current time. -var timeNow = time.Now diff --git a/vendor/golang.org/x/crypto/acme/autocert/autocert.go b/vendor/golang.org/x/crypto/acme/autocert/autocert.go deleted file mode 100644 index 453e72291..000000000 --- a/vendor/golang.org/x/crypto/acme/autocert/autocert.go +++ /dev/null @@ -1,973 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package autocert provides automatic access to certificates from Let's Encrypt -// and any other ACME-based CA. -// -// This package is a work in progress and makes no API stability promises. -package autocert - -import ( - "bytes" - "context" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "errors" - "fmt" - "io" - mathrand "math/rand" - "net" - "net/http" - "path" - "strconv" - "strings" - "sync" - "time" - - "golang.org/x/crypto/acme" -) - -// createCertRetryAfter is how much time to wait before removing a failed state -// entry due to an unsuccessful createCert call. -// This is a variable instead of a const for testing. -// TODO: Consider making it configurable or an exp backoff? -var createCertRetryAfter = time.Minute - -// pseudoRand is safe for concurrent use. -var pseudoRand *lockedMathRand - -func init() { - src := mathrand.NewSource(timeNow().UnixNano()) - pseudoRand = &lockedMathRand{rnd: mathrand.New(src)} -} - -// AcceptTOS is a Manager.Prompt function that always returns true to -// indicate acceptance of the CA's Terms of Service during account -// registration. -func AcceptTOS(tosURL string) bool { return true } - -// HostPolicy specifies which host names the Manager is allowed to respond to. -// It returns a non-nil error if the host should be rejected. -// The returned error is accessible via tls.Conn.Handshake and its callers. -// See Manager's HostPolicy field and GetCertificate method docs for more details. -type HostPolicy func(ctx context.Context, host string) error - -// HostWhitelist returns a policy where only the specified host names are allowed. -// Only exact matches are currently supported. Subdomains, regexp or wildcard -// will not match. -func HostWhitelist(hosts ...string) HostPolicy { - whitelist := make(map[string]bool, len(hosts)) - for _, h := range hosts { - whitelist[h] = true - } - return func(_ context.Context, host string) error { - if !whitelist[host] { - return errors.New("acme/autocert: host not configured") - } - return nil - } -} - -// defaultHostPolicy is used when Manager.HostPolicy is not set. -func defaultHostPolicy(context.Context, string) error { - return nil -} - -// Manager is a stateful certificate manager built on top of acme.Client. -// It obtains and refreshes certificates automatically using "tls-sni-01", -// "tls-sni-02" and "http-01" challenge types, as well as providing them -// to a TLS server via tls.Config. -// -// You must specify a cache implementation, such as DirCache, -// to reuse obtained certificates across program restarts. -// Otherwise your server is very likely to exceed the certificate -// issuer's request rate limits. -type Manager struct { - // Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS). - // The registration may require the caller to agree to the CA's TOS. - // If so, Manager calls Prompt with a TOS URL provided by the CA. Prompt should report - // whether the caller agrees to the terms. - // - // To always accept the terms, the callers can use AcceptTOS. - Prompt func(tosURL string) bool - - // Cache optionally stores and retrieves previously-obtained certificates. - // If nil, certs will only be cached for the lifetime of the Manager. - // - // Manager passes the Cache certificates data encoded in PEM, with private/public - // parts combined in a single Cache.Put call, private key first. - Cache Cache - - // HostPolicy controls which domains the Manager will attempt - // to retrieve new certificates for. It does not affect cached certs. - // - // If non-nil, HostPolicy is called before requesting a new cert. - // If nil, all hosts are currently allowed. This is not recommended, - // as it opens a potential attack where clients connect to a server - // by IP address and pretend to be asking for an incorrect host name. - // Manager will attempt to obtain a certificate for that host, incorrectly, - // eventually reaching the CA's rate limit for certificate requests - // and making it impossible to obtain actual certificates. - // - // See GetCertificate for more details. - HostPolicy HostPolicy - - // RenewBefore optionally specifies how early certificates should - // be renewed before they expire. - // - // If zero, they're renewed 30 days before expiration. - RenewBefore time.Duration - - // Client is used to perform low-level operations, such as account registration - // and requesting new certificates. - // If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL - // directory endpoint and a newly-generated ECDSA P-256 key. - // - // Mutating the field after the first call of GetCertificate method will have no effect. - Client *acme.Client - - // Email optionally specifies a contact email address. - // This is used by CAs, such as Let's Encrypt, to notify about problems - // with issued certificates. - // - // If the Client's account key is already registered, Email is not used. - Email string - - // ForceRSA makes the Manager generate certificates with 2048-bit RSA keys. - // - // If false, a default is used. Currently the default - // is EC-based keys using the P-256 curve. - ForceRSA bool - - clientMu sync.Mutex - client *acme.Client // initialized by acmeClient method - - stateMu sync.Mutex - state map[string]*certState // keyed by domain name - - // renewal tracks the set of domains currently running renewal timers. - // It is keyed by domain name. - renewalMu sync.Mutex - renewal map[string]*domainRenewal - - // tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens. - tokensMu sync.RWMutex - // tryHTTP01 indicates whether the Manager should try "http-01" challenge type - // during the authorization flow. - tryHTTP01 bool - // httpTokens contains response body values for http-01 challenges - // and is keyed by the URL path at which a challenge response is expected - // to be provisioned. - // The entries are stored for the duration of the authorization flow. - httpTokens map[string][]byte - // certTokens contains temporary certificates for tls-sni challenges - // and is keyed by token domain name, which matches server name of ClientHello. - // Keys always have ".acme.invalid" suffix. - // The entries are stored for the duration of the authorization flow. - certTokens map[string]*tls.Certificate -} - -// GetCertificate implements the tls.Config.GetCertificate hook. -// It provides a TLS certificate for hello.ServerName host, including answering -// *.acme.invalid (TLS-SNI) challenges. All other fields of hello are ignored. -// -// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting -// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation. -// The error is propagated back to the caller of GetCertificate and is user-visible. -// This does not affect cached certs. See HostPolicy field description for more details. -func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { - if m.Prompt == nil { - return nil, errors.New("acme/autocert: Manager.Prompt not set") - } - - name := hello.ServerName - if name == "" { - return nil, errors.New("acme/autocert: missing server name") - } - if !strings.Contains(strings.Trim(name, "."), ".") { - return nil, errors.New("acme/autocert: server name component count invalid") - } - if strings.ContainsAny(name, `/\`) { - return nil, errors.New("acme/autocert: server name contains invalid character") - } - - // In the worst-case scenario, the timeout needs to account for caching, host policy, - // domain ownership verification and certificate issuance. - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - // check whether this is a token cert requested for TLS-SNI challenge - if strings.HasSuffix(name, ".acme.invalid") { - m.tokensMu.RLock() - defer m.tokensMu.RUnlock() - if cert := m.certTokens[name]; cert != nil { - return cert, nil - } - if cert, err := m.cacheGet(ctx, name); err == nil { - return cert, nil - } - // TODO: cache error results? - return nil, fmt.Errorf("acme/autocert: no token cert for %q", name) - } - - // regular domain - name = strings.TrimSuffix(name, ".") // golang.org/issue/18114 - cert, err := m.cert(ctx, name) - if err == nil { - return cert, nil - } - if err != ErrCacheMiss { - return nil, err - } - - // first-time - if err := m.hostPolicy()(ctx, name); err != nil { - return nil, err - } - cert, err = m.createCert(ctx, name) - if err != nil { - return nil, err - } - m.cachePut(ctx, name, cert) - return cert, nil -} - -// HTTPHandler configures the Manager to provision ACME "http-01" challenge responses. -// It returns an http.Handler that responds to the challenges and must be -// running on port 80. If it receives a request that is not an ACME challenge, -// it delegates the request to the optional fallback handler. -// -// If fallback is nil, the returned handler redirects all GET and HEAD requests -// to the default TLS port 443 with 302 Found status code, preserving the original -// request path and query. It responds with 400 Bad Request to all other HTTP methods. -// The fallback is not protected by the optional HostPolicy. -// -// Because the fallback handler is run with unencrypted port 80 requests, -// the fallback should not serve TLS-only requests. -// -// If HTTPHandler is never called, the Manager will only use TLS SNI -// challenges for domain verification. -func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler { - m.tokensMu.Lock() - defer m.tokensMu.Unlock() - m.tryHTTP01 = true - - if fallback == nil { - fallback = http.HandlerFunc(handleHTTPRedirect) - } - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !strings.HasPrefix(r.URL.Path, "/.well-known/acme-challenge/") { - fallback.ServeHTTP(w, r) - return - } - // A reasonable context timeout for cache and host policy only, - // because we don't wait for a new certificate issuance here. - ctx, cancel := context.WithTimeout(r.Context(), time.Minute) - defer cancel() - if err := m.hostPolicy()(ctx, r.Host); err != nil { - http.Error(w, err.Error(), http.StatusForbidden) - return - } - data, err := m.httpToken(ctx, r.URL.Path) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - w.Write(data) - }) -} - -func handleHTTPRedirect(w http.ResponseWriter, r *http.Request) { - if r.Method != "GET" && r.Method != "HEAD" { - http.Error(w, "Use HTTPS", http.StatusBadRequest) - return - } - target := "https://" + stripPort(r.Host) + r.URL.RequestURI() - http.Redirect(w, r, target, http.StatusFound) -} - -func stripPort(hostport string) string { - host, _, err := net.SplitHostPort(hostport) - if err != nil { - return hostport - } - return net.JoinHostPort(host, "443") -} - -// cert returns an existing certificate either from m.state or cache. -// If a certificate is found in cache but not in m.state, the latter will be filled -// with the cached value. -func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, error) { - m.stateMu.Lock() - if s, ok := m.state[name]; ok { - m.stateMu.Unlock() - s.RLock() - defer s.RUnlock() - return s.tlscert() - } - defer m.stateMu.Unlock() - cert, err := m.cacheGet(ctx, name) - if err != nil { - return nil, err - } - signer, ok := cert.PrivateKey.(crypto.Signer) - if !ok { - return nil, errors.New("acme/autocert: private key cannot sign") - } - if m.state == nil { - m.state = make(map[string]*certState) - } - s := &certState{ - key: signer, - cert: cert.Certificate, - leaf: cert.Leaf, - } - m.state[name] = s - go m.renew(name, s.key, s.leaf.NotAfter) - return cert, nil -} - -// cacheGet always returns a valid certificate, or an error otherwise. -// If a cached certficate exists but is not valid, ErrCacheMiss is returned. -func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) { - if m.Cache == nil { - return nil, ErrCacheMiss - } - data, err := m.Cache.Get(ctx, domain) - if err != nil { - return nil, err - } - - // private - priv, pub := pem.Decode(data) - if priv == nil || !strings.Contains(priv.Type, "PRIVATE") { - return nil, ErrCacheMiss - } - privKey, err := parsePrivateKey(priv.Bytes) - if err != nil { - return nil, err - } - - // public - var pubDER [][]byte - for len(pub) > 0 { - var b *pem.Block - b, pub = pem.Decode(pub) - if b == nil { - break - } - pubDER = append(pubDER, b.Bytes) - } - if len(pub) > 0 { - // Leftover content not consumed by pem.Decode. Corrupt. Ignore. - return nil, ErrCacheMiss - } - - // verify and create TLS cert - leaf, err := validCert(domain, pubDER, privKey) - if err != nil { - return nil, ErrCacheMiss - } - tlscert := &tls.Certificate{ - Certificate: pubDER, - PrivateKey: privKey, - Leaf: leaf, - } - return tlscert, nil -} - -func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Certificate) error { - if m.Cache == nil { - return nil - } - - // contains PEM-encoded data - var buf bytes.Buffer - - // private - switch key := tlscert.PrivateKey.(type) { - case *ecdsa.PrivateKey: - if err := encodeECDSAKey(&buf, key); err != nil { - return err - } - case *rsa.PrivateKey: - b := x509.MarshalPKCS1PrivateKey(key) - pb := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: b} - if err := pem.Encode(&buf, pb); err != nil { - return err - } - default: - return errors.New("acme/autocert: unknown private key type") - } - - // public - for _, b := range tlscert.Certificate { - pb := &pem.Block{Type: "CERTIFICATE", Bytes: b} - if err := pem.Encode(&buf, pb); err != nil { - return err - } - } - - return m.Cache.Put(ctx, domain, buf.Bytes()) -} - -func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error { - b, err := x509.MarshalECPrivateKey(key) - if err != nil { - return err - } - pb := &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} - return pem.Encode(w, pb) -} - -// createCert starts the domain ownership verification and returns a certificate -// for that domain upon success. -// -// If the domain is already being verified, it waits for the existing verification to complete. -// Either way, createCert blocks for the duration of the whole process. -func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certificate, error) { - // TODO: maybe rewrite this whole piece using sync.Once - state, err := m.certState(domain) - if err != nil { - return nil, err - } - // state may exist if another goroutine is already working on it - // in which case just wait for it to finish - if !state.locked { - state.RLock() - defer state.RUnlock() - return state.tlscert() - } - - // We are the first; state is locked. - // Unblock the readers when domain ownership is verified - // and we got the cert or the process failed. - defer state.Unlock() - state.locked = false - - der, leaf, err := m.authorizedCert(ctx, state.key, domain) - if err != nil { - // Remove the failed state after some time, - // making the manager call createCert again on the following TLS hello. - time.AfterFunc(createCertRetryAfter, func() { - defer testDidRemoveState(domain) - m.stateMu.Lock() - defer m.stateMu.Unlock() - // Verify the state hasn't changed and it's still invalid - // before deleting. - s, ok := m.state[domain] - if !ok { - return - } - if _, err := validCert(domain, s.cert, s.key); err == nil { - return - } - delete(m.state, domain) - }) - return nil, err - } - state.cert = der - state.leaf = leaf - go m.renew(domain, state.key, state.leaf.NotAfter) - return state.tlscert() -} - -// certState returns a new or existing certState. -// If a new certState is returned, state.exist is false and the state is locked. -// The returned error is non-nil only in the case where a new state could not be created. -func (m *Manager) certState(domain string) (*certState, error) { - m.stateMu.Lock() - defer m.stateMu.Unlock() - if m.state == nil { - m.state = make(map[string]*certState) - } - // existing state - if state, ok := m.state[domain]; ok { - return state, nil - } - - // new locked state - var ( - err error - key crypto.Signer - ) - if m.ForceRSA { - key, err = rsa.GenerateKey(rand.Reader, 2048) - } else { - key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - } - if err != nil { - return nil, err - } - - state := &certState{ - key: key, - locked: true, - } - state.Lock() // will be unlocked by m.certState caller - m.state[domain] = state - return state, nil -} - -// authorizedCert starts the domain ownership verification process and requests a new cert upon success. -// The key argument is the certificate private key. -func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) { - client, err := m.acmeClient(ctx) - if err != nil { - return nil, nil, err - } - - if err := m.verify(ctx, client, domain); err != nil { - return nil, nil, err - } - csr, err := certRequest(key, domain) - if err != nil { - return nil, nil, err - } - der, _, err = client.CreateCert(ctx, csr, 0, true) - if err != nil { - return nil, nil, err - } - leaf, err = validCert(domain, der, key) - if err != nil { - return nil, nil, err - } - return der, leaf, nil -} - -// verify runs the identifier (domain) authorization flow -// using each applicable ACME challenge type. -func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error { - // The list of challenge types we'll try to fulfill - // in this specific order. - challengeTypes := []string{"tls-sni-02", "tls-sni-01"} - m.tokensMu.RLock() - if m.tryHTTP01 { - challengeTypes = append(challengeTypes, "http-01") - } - m.tokensMu.RUnlock() - - var nextTyp int // challengeType index of the next challenge type to try - for { - // Start domain authorization and get the challenge. - authz, err := client.Authorize(ctx, domain) - if err != nil { - return err - } - // No point in accepting challenges if the authorization status - // is in a final state. - switch authz.Status { - case acme.StatusValid: - return nil // already authorized - case acme.StatusInvalid: - return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI) - } - - // Pick the next preferred challenge. - var chal *acme.Challenge - for chal == nil && nextTyp < len(challengeTypes) { - chal = pickChallenge(challengeTypes[nextTyp], authz.Challenges) - nextTyp++ - } - if chal == nil { - return fmt.Errorf("acme/autocert: unable to authorize %q; tried %q", domain, challengeTypes) - } - cleanup, err := m.fulfill(ctx, client, chal) - if err != nil { - continue - } - defer cleanup() - if _, err := client.Accept(ctx, chal); err != nil { - continue - } - - // A challenge is fulfilled and accepted: wait for the CA to validate. - if _, err := client.WaitAuthorization(ctx, authz.URI); err == nil { - return nil - } - } -} - -// fulfill provisions a response to the challenge chal. -// The cleanup is non-nil only if provisioning succeeded. -func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge) (cleanup func(), err error) { - switch chal.Type { - case "tls-sni-01": - cert, name, err := client.TLSSNI01ChallengeCert(chal.Token) - if err != nil { - return nil, err - } - m.putCertToken(ctx, name, &cert) - return func() { go m.deleteCertToken(name) }, nil - case "tls-sni-02": - cert, name, err := client.TLSSNI02ChallengeCert(chal.Token) - if err != nil { - return nil, err - } - m.putCertToken(ctx, name, &cert) - return func() { go m.deleteCertToken(name) }, nil - case "http-01": - resp, err := client.HTTP01ChallengeResponse(chal.Token) - if err != nil { - return nil, err - } - p := client.HTTP01ChallengePath(chal.Token) - m.putHTTPToken(ctx, p, resp) - return func() { go m.deleteHTTPToken(p) }, nil - } - return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type) -} - -func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge { - for _, c := range chal { - if c.Type == typ { - return c - } - } - return nil -} - -// putCertToken stores the cert under the named key in both m.certTokens map -// and m.Cache. -func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) { - m.tokensMu.Lock() - defer m.tokensMu.Unlock() - if m.certTokens == nil { - m.certTokens = make(map[string]*tls.Certificate) - } - m.certTokens[name] = cert - m.cachePut(ctx, name, cert) -} - -// deleteCertToken removes the token certificate for the specified domain name -// from both m.certTokens map and m.Cache. -func (m *Manager) deleteCertToken(name string) { - m.tokensMu.Lock() - defer m.tokensMu.Unlock() - delete(m.certTokens, name) - if m.Cache != nil { - m.Cache.Delete(context.Background(), name) - } -} - -// httpToken retrieves an existing http-01 token value from an in-memory map -// or the optional cache. -func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) { - m.tokensMu.RLock() - defer m.tokensMu.RUnlock() - if v, ok := m.httpTokens[tokenPath]; ok { - return v, nil - } - if m.Cache == nil { - return nil, fmt.Errorf("acme/autocert: no token at %q", tokenPath) - } - return m.Cache.Get(ctx, httpTokenCacheKey(tokenPath)) -} - -// putHTTPToken stores an http-01 token value using tokenPath as key -// in both in-memory map and the optional Cache. -// -// It ignores any error returned from Cache.Put. -func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) { - m.tokensMu.Lock() - defer m.tokensMu.Unlock() - if m.httpTokens == nil { - m.httpTokens = make(map[string][]byte) - } - b := []byte(val) - m.httpTokens[tokenPath] = b - if m.Cache != nil { - m.Cache.Put(ctx, httpTokenCacheKey(tokenPath), b) - } -} - -// deleteHTTPToken removes an http-01 token value from both in-memory map -// and the optional Cache, ignoring any error returned from the latter. -// -// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout. -func (m *Manager) deleteHTTPToken(tokenPath string) { - m.tokensMu.Lock() - defer m.tokensMu.Unlock() - delete(m.httpTokens, tokenPath) - if m.Cache != nil { - m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath)) - } -} - -// httpTokenCacheKey returns a key at which an http-01 token value may be stored -// in the Manager's optional Cache. -func httpTokenCacheKey(tokenPath string) string { - return "http-01-" + path.Base(tokenPath) -} - -// renew starts a cert renewal timer loop, one per domain. -// -// The loop is scheduled in two cases: -// - a cert was fetched from cache for the first time (wasn't in m.state) -// - a new cert was created by m.createCert -// -// The key argument is a certificate private key. -// The exp argument is the cert expiration time (NotAfter). -func (m *Manager) renew(domain string, key crypto.Signer, exp time.Time) { - m.renewalMu.Lock() - defer m.renewalMu.Unlock() - if m.renewal[domain] != nil { - // another goroutine is already on it - return - } - if m.renewal == nil { - m.renewal = make(map[string]*domainRenewal) - } - dr := &domainRenewal{m: m, domain: domain, key: key} - m.renewal[domain] = dr - dr.start(exp) -} - -// stopRenew stops all currently running cert renewal timers. -// The timers are not restarted during the lifetime of the Manager. -func (m *Manager) stopRenew() { - m.renewalMu.Lock() - defer m.renewalMu.Unlock() - for name, dr := range m.renewal { - delete(m.renewal, name) - dr.stop() - } -} - -func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) { - const keyName = "acme_account.key" - - genKey := func() (*ecdsa.PrivateKey, error) { - return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - } - - if m.Cache == nil { - return genKey() - } - - data, err := m.Cache.Get(ctx, keyName) - if err == ErrCacheMiss { - key, err := genKey() - if err != nil { - return nil, err - } - var buf bytes.Buffer - if err := encodeECDSAKey(&buf, key); err != nil { - return nil, err - } - if err := m.Cache.Put(ctx, keyName, buf.Bytes()); err != nil { - return nil, err - } - return key, nil - } - if err != nil { - return nil, err - } - - priv, _ := pem.Decode(data) - if priv == nil || !strings.Contains(priv.Type, "PRIVATE") { - return nil, errors.New("acme/autocert: invalid account key found in cache") - } - return parsePrivateKey(priv.Bytes) -} - -func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) { - m.clientMu.Lock() - defer m.clientMu.Unlock() - if m.client != nil { - return m.client, nil - } - - client := m.Client - if client == nil { - client = &acme.Client{DirectoryURL: acme.LetsEncryptURL} - } - if client.Key == nil { - var err error - client.Key, err = m.accountKey(ctx) - if err != nil { - return nil, err - } - } - var contact []string - if m.Email != "" { - contact = []string{"mailto:" + m.Email} - } - a := &acme.Account{Contact: contact} - _, err := client.Register(ctx, a, m.Prompt) - if ae, ok := err.(*acme.Error); err == nil || ok && ae.StatusCode == http.StatusConflict { - // conflict indicates the key is already registered - m.client = client - err = nil - } - return m.client, err -} - -func (m *Manager) hostPolicy() HostPolicy { - if m.HostPolicy != nil { - return m.HostPolicy - } - return defaultHostPolicy -} - -func (m *Manager) renewBefore() time.Duration { - if m.RenewBefore > renewJitter { - return m.RenewBefore - } - return 720 * time.Hour // 30 days -} - -// certState is ready when its mutex is unlocked for reading. -type certState struct { - sync.RWMutex - locked bool // locked for read/write - key crypto.Signer // private key for cert - cert [][]byte // DER encoding - leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil -} - -// tlscert creates a tls.Certificate from s.key and s.cert. -// Callers should wrap it in s.RLock() and s.RUnlock(). -func (s *certState) tlscert() (*tls.Certificate, error) { - if s.key == nil { - return nil, errors.New("acme/autocert: missing signer") - } - if len(s.cert) == 0 { - return nil, errors.New("acme/autocert: missing certificate") - } - return &tls.Certificate{ - PrivateKey: s.key, - Certificate: s.cert, - Leaf: s.leaf, - }, nil -} - -// certRequest creates a certificate request for the given common name cn -// and optional SANs. -func certRequest(key crypto.Signer, cn string, san ...string) ([]byte, error) { - req := &x509.CertificateRequest{ - Subject: pkix.Name{CommonName: cn}, - DNSNames: san, - } - return x509.CreateCertificateRequest(rand.Reader, req, key) -} - -// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates -// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys. -// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. -// -// Inspired by parsePrivateKey in crypto/tls/tls.go. -func parsePrivateKey(der []byte) (crypto.Signer, error) { - if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { - return key, nil - } - if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { - switch key := key.(type) { - case *rsa.PrivateKey: - return key, nil - case *ecdsa.PrivateKey: - return key, nil - default: - return nil, errors.New("acme/autocert: unknown private key type in PKCS#8 wrapping") - } - } - if key, err := x509.ParseECPrivateKey(der); err == nil { - return key, nil - } - - return nil, errors.New("acme/autocert: failed to parse private key") -} - -// validCert parses a cert chain provided as der argument and verifies the leaf, der[0], -// corresponds to the private key, as well as the domain match and expiration dates. -// It doesn't do any revocation checking. -// -// The returned value is the verified leaf cert. -func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certificate, err error) { - // parse public part(s) - var n int - for _, b := range der { - n += len(b) - } - pub := make([]byte, n) - n = 0 - for _, b := range der { - n += copy(pub[n:], b) - } - x509Cert, err := x509.ParseCertificates(pub) - if len(x509Cert) == 0 { - return nil, errors.New("acme/autocert: no public key found") - } - // verify the leaf is not expired and matches the domain name - leaf = x509Cert[0] - now := timeNow() - if now.Before(leaf.NotBefore) { - return nil, errors.New("acme/autocert: certificate is not valid yet") - } - if now.After(leaf.NotAfter) { - return nil, errors.New("acme/autocert: expired certificate") - } - if err := leaf.VerifyHostname(domain); err != nil { - return nil, err - } - // ensure the leaf corresponds to the private key - switch pub := leaf.PublicKey.(type) { - case *rsa.PublicKey: - prv, ok := key.(*rsa.PrivateKey) - if !ok { - return nil, errors.New("acme/autocert: private key type does not match public key type") - } - if pub.N.Cmp(prv.N) != 0 { - return nil, errors.New("acme/autocert: private key does not match public key") - } - case *ecdsa.PublicKey: - prv, ok := key.(*ecdsa.PrivateKey) - if !ok { - return nil, errors.New("acme/autocert: private key type does not match public key type") - } - if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 { - return nil, errors.New("acme/autocert: private key does not match public key") - } - default: - return nil, errors.New("acme/autocert: unknown public key algorithm") - } - return leaf, nil -} - -func retryAfter(v string) time.Duration { - if i, err := strconv.Atoi(v); err == nil { - return time.Duration(i) * time.Second - } - if t, err := http.ParseTime(v); err == nil { - return t.Sub(timeNow()) - } - return time.Second -} - -type lockedMathRand struct { - sync.Mutex - rnd *mathrand.Rand -} - -func (r *lockedMathRand) int63n(max int64) int64 { - r.Lock() - n := r.rnd.Int63n(max) - r.Unlock() - return n -} - -// For easier testing. -var ( - timeNow = time.Now - - // Called when a state is removed. - testDidRemoveState = func(domain string) {} -) diff --git a/vendor/golang.org/x/crypto/acme/autocert/cache.go b/vendor/golang.org/x/crypto/acme/autocert/cache.go deleted file mode 100644 index 61a5fd239..000000000 --- a/vendor/golang.org/x/crypto/acme/autocert/cache.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package autocert - -import ( - "context" - "errors" - "io/ioutil" - "os" - "path/filepath" -) - -// ErrCacheMiss is returned when a certificate is not found in cache. -var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss") - -// Cache is used by Manager to store and retrieve previously obtained certificates -// as opaque data. -// -// The key argument of the methods refers to a domain name but need not be an FQDN. -// Cache implementations should not rely on the key naming pattern. -type Cache interface { - // Get returns a certificate data for the specified key. - // If there's no such key, Get returns ErrCacheMiss. - Get(ctx context.Context, key string) ([]byte, error) - - // Put stores the data in the cache under the specified key. - // Underlying implementations may use any data storage format, - // as long as the reverse operation, Get, results in the original data. - Put(ctx context.Context, key string, data []byte) error - - // Delete removes a certificate data from the cache under the specified key. - // If there's no such key in the cache, Delete returns nil. - Delete(ctx context.Context, key string) error -} - -// DirCache implements Cache using a directory on the local filesystem. -// If the directory does not exist, it will be created with 0700 permissions. -type DirCache string - -// Get reads a certificate data from the specified file name. -func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) { - name = filepath.Join(string(d), name) - var ( - data []byte - err error - done = make(chan struct{}) - ) - go func() { - data, err = ioutil.ReadFile(name) - close(done) - }() - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-done: - } - if os.IsNotExist(err) { - return nil, ErrCacheMiss - } - return data, err -} - -// Put writes the certificate data to the specified file name. -// The file will be created with 0600 permissions. -func (d DirCache) Put(ctx context.Context, name string, data []byte) error { - if err := os.MkdirAll(string(d), 0700); err != nil { - return err - } - - done := make(chan struct{}) - var err error - go func() { - defer close(done) - var tmp string - if tmp, err = d.writeTempFile(name, data); err != nil { - return - } - select { - case <-ctx.Done(): - // Don't overwrite the file if the context was canceled. - default: - newName := filepath.Join(string(d), name) - err = os.Rename(tmp, newName) - } - }() - select { - case <-ctx.Done(): - return ctx.Err() - case <-done: - } - return err -} - -// Delete removes the specified file name. -func (d DirCache) Delete(ctx context.Context, name string) error { - name = filepath.Join(string(d), name) - var ( - err error - done = make(chan struct{}) - ) - go func() { - err = os.Remove(name) - close(done) - }() - select { - case <-ctx.Done(): - return ctx.Err() - case <-done: - } - if err != nil && !os.IsNotExist(err) { - return err - } - return nil -} - -// writeTempFile writes b to a temporary file, closes the file and returns its path. -func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) { - // TempFile uses 0600 permissions - f, err := ioutil.TempFile(string(d), prefix) - if err != nil { - return "", err - } - if _, err := f.Write(b); err != nil { - f.Close() - return "", err - } - return f.Name(), f.Close() -} diff --git a/vendor/golang.org/x/crypto/acme/autocert/listener.go b/vendor/golang.org/x/crypto/acme/autocert/listener.go deleted file mode 100644 index d744df0ed..000000000 --- a/vendor/golang.org/x/crypto/acme/autocert/listener.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package autocert - -import ( - "crypto/tls" - "log" - "net" - "os" - "path/filepath" - "runtime" - "time" -) - -// NewListener returns a net.Listener that listens on the standard TLS -// port (443) on all interfaces and returns *tls.Conn connections with -// LetsEncrypt certificates for the provided domain or domains. -// -// It enables one-line HTTPS servers: -// -// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler)) -// -// NewListener is a convenience function for a common configuration. -// More complex or custom configurations can use the autocert.Manager -// type instead. -// -// Use of this function implies acceptance of the LetsEncrypt Terms of -// Service. If domains is not empty, the provided domains are passed -// to HostWhitelist. If domains is empty, the listener will do -// LetsEncrypt challenges for any requested domain, which is not -// recommended. -// -// Certificates are cached in a "golang-autocert" directory under an -// operating system-specific cache or temp directory. This may not -// be suitable for servers spanning multiple machines. -// -// The returned listener uses a *tls.Config that enables HTTP/2, and -// should only be used with servers that support HTTP/2. -// -// The returned Listener also enables TCP keep-alives on the accepted -// connections. The returned *tls.Conn are returned before their TLS -// handshake has completed. -func NewListener(domains ...string) net.Listener { - m := &Manager{ - Prompt: AcceptTOS, - } - if len(domains) > 0 { - m.HostPolicy = HostWhitelist(domains...) - } - dir := cacheDir() - if err := os.MkdirAll(dir, 0700); err != nil { - log.Printf("warning: autocert.NewListener not using a cache: %v", err) - } else { - m.Cache = DirCache(dir) - } - return m.Listener() -} - -// Listener listens on the standard TLS port (443) on all interfaces -// and returns a net.Listener returning *tls.Conn connections. -// -// The returned listener uses a *tls.Config that enables HTTP/2, and -// should only be used with servers that support HTTP/2. -// -// The returned Listener also enables TCP keep-alives on the accepted -// connections. The returned *tls.Conn are returned before their TLS -// handshake has completed. -// -// Unlike NewListener, it is the caller's responsibility to initialize -// the Manager m's Prompt, Cache, HostPolicy, and other desired options. -func (m *Manager) Listener() net.Listener { - ln := &listener{ - m: m, - conf: &tls.Config{ - GetCertificate: m.GetCertificate, // bonus: panic on nil m - NextProtos: []string{"h2", "http/1.1"}, // Enable HTTP/2 - }, - } - ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443") - return ln -} - -type listener struct { - m *Manager - conf *tls.Config - - tcpListener net.Listener - tcpListenErr error -} - -func (ln *listener) Accept() (net.Conn, error) { - if ln.tcpListenErr != nil { - return nil, ln.tcpListenErr - } - conn, err := ln.tcpListener.Accept() - if err != nil { - return nil, err - } - tcpConn := conn.(*net.TCPConn) - - // Because Listener is a convenience function, help out with - // this too. This is not possible for the caller to set once - // we return a *tcp.Conn wrapping an inaccessible net.Conn. - // If callers don't want this, they can do things the manual - // way and tweak as needed. But this is what net/http does - // itself, so copy that. If net/http changes, we can change - // here too. - tcpConn.SetKeepAlive(true) - tcpConn.SetKeepAlivePeriod(3 * time.Minute) - - return tls.Server(tcpConn, ln.conf), nil -} - -func (ln *listener) Addr() net.Addr { - if ln.tcpListener != nil { - return ln.tcpListener.Addr() - } - // net.Listen failed. Return something non-nil in case callers - // call Addr before Accept: - return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443} -} - -func (ln *listener) Close() error { - if ln.tcpListenErr != nil { - return ln.tcpListenErr - } - return ln.tcpListener.Close() -} - -func homeDir() string { - if runtime.GOOS == "windows" { - return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") - } - if h := os.Getenv("HOME"); h != "" { - return h - } - return "/" -} - -func cacheDir() string { - const base = "golang-autocert" - switch runtime.GOOS { - case "darwin": - return filepath.Join(homeDir(), "Library", "Caches", base) - case "windows": - for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} { - if v := os.Getenv(ev); v != "" { - return filepath.Join(v, base) - } - } - // Worst case: - return filepath.Join(homeDir(), base) - } - if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" { - return filepath.Join(xdg, base) - } - return filepath.Join(homeDir(), ".cache", base) -} diff --git a/vendor/golang.org/x/crypto/acme/autocert/renewal.go b/vendor/golang.org/x/crypto/acme/autocert/renewal.go deleted file mode 100644 index 6c5da2bc8..000000000 --- a/vendor/golang.org/x/crypto/acme/autocert/renewal.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package autocert - -import ( - "context" - "crypto" - "sync" - "time" -) - -// renewJitter is the maximum deviation from Manager.RenewBefore. -const renewJitter = time.Hour - -// domainRenewal tracks the state used by the periodic timers -// renewing a single domain's cert. -type domainRenewal struct { - m *Manager - domain string - key crypto.Signer - - timerMu sync.Mutex - timer *time.Timer -} - -// start starts a cert renewal timer at the time -// defined by the certificate expiration time exp. -// -// If the timer is already started, calling start is a noop. -func (dr *domainRenewal) start(exp time.Time) { - dr.timerMu.Lock() - defer dr.timerMu.Unlock() - if dr.timer != nil { - return - } - dr.timer = time.AfterFunc(dr.next(exp), dr.renew) -} - -// stop stops the cert renewal timer. -// If the timer is already stopped, calling stop is a noop. -func (dr *domainRenewal) stop() { - dr.timerMu.Lock() - defer dr.timerMu.Unlock() - if dr.timer == nil { - return - } - dr.timer.Stop() - dr.timer = nil -} - -// renew is called periodically by a timer. -// The first renew call is kicked off by dr.start. -func (dr *domainRenewal) renew() { - dr.timerMu.Lock() - defer dr.timerMu.Unlock() - if dr.timer == nil { - return - } - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) - defer cancel() - // TODO: rotate dr.key at some point? - next, err := dr.do(ctx) - if err != nil { - next = renewJitter / 2 - next += time.Duration(pseudoRand.int63n(int64(next))) - } - dr.timer = time.AfterFunc(next, dr.renew) - testDidRenewLoop(next, err) -} - -// do is similar to Manager.createCert but it doesn't lock a Manager.state item. -// Instead, it requests a new certificate independently and, upon success, -// replaces dr.m.state item with a new one and updates cache for the given domain. -// -// It may return immediately if the expiration date of the currently cached cert -// is far enough in the future. -// -// The returned value is a time interval after which the renewal should occur again. -func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { - // a race is likely unavoidable in a distributed environment - // but we try nonetheless - if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil { - next := dr.next(tlscert.Leaf.NotAfter) - if next > dr.m.renewBefore()+renewJitter { - return next, nil - } - } - - der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.domain) - if err != nil { - return 0, err - } - state := &certState{ - key: dr.key, - cert: der, - leaf: leaf, - } - tlscert, err := state.tlscert() - if err != nil { - return 0, err - } - dr.m.cachePut(ctx, dr.domain, tlscert) - dr.m.stateMu.Lock() - defer dr.m.stateMu.Unlock() - // m.state is guaranteed to be non-nil at this point - dr.m.state[dr.domain] = state - return dr.next(leaf.NotAfter), nil -} - -func (dr *domainRenewal) next(expiry time.Time) time.Duration { - d := expiry.Sub(timeNow()) - dr.m.renewBefore() - // add a bit of randomness to renew deadline - n := pseudoRand.int63n(int64(renewJitter)) - d -= time.Duration(n) - if d < 0 { - return 0 - } - return d -} - -var testDidRenewLoop = func(next time.Duration, err error) {} diff --git a/vendor/golang.org/x/crypto/acme/jws.go b/vendor/golang.org/x/crypto/acme/jws.go deleted file mode 100644 index 6cbca25de..000000000 --- a/vendor/golang.org/x/crypto/acme/jws.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package acme - -import ( - "crypto" - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - _ "crypto/sha512" // need for EC keys - "encoding/base64" - "encoding/json" - "fmt" - "math/big" -) - -// jwsEncodeJSON signs claimset using provided key and a nonce. -// The result is serialized in JSON format. -// See https://tools.ietf.org/html/rfc7515#section-7. -func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byte, error) { - jwk, err := jwkEncode(key.Public()) - if err != nil { - return nil, err - } - alg, sha := jwsHasher(key) - if alg == "" || !sha.Available() { - return nil, ErrUnsupportedKey - } - phead := fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q}`, alg, jwk, nonce) - phead = base64.RawURLEncoding.EncodeToString([]byte(phead)) - cs, err := json.Marshal(claimset) - if err != nil { - return nil, err - } - payload := base64.RawURLEncoding.EncodeToString(cs) - hash := sha.New() - hash.Write([]byte(phead + "." + payload)) - sig, err := jwsSign(key, sha, hash.Sum(nil)) - if err != nil { - return nil, err - } - - enc := struct { - Protected string `json:"protected"` - Payload string `json:"payload"` - Sig string `json:"signature"` - }{ - Protected: phead, - Payload: payload, - Sig: base64.RawURLEncoding.EncodeToString(sig), - } - return json.Marshal(&enc) -} - -// jwkEncode encodes public part of an RSA or ECDSA key into a JWK. -// The result is also suitable for creating a JWK thumbprint. -// https://tools.ietf.org/html/rfc7517 -func jwkEncode(pub crypto.PublicKey) (string, error) { - switch pub := pub.(type) { - case *rsa.PublicKey: - // https://tools.ietf.org/html/rfc7518#section-6.3.1 - n := pub.N - e := big.NewInt(int64(pub.E)) - // Field order is important. - // See https://tools.ietf.org/html/rfc7638#section-3.3 for details. - return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`, - base64.RawURLEncoding.EncodeToString(e.Bytes()), - base64.RawURLEncoding.EncodeToString(n.Bytes()), - ), nil - case *ecdsa.PublicKey: - // https://tools.ietf.org/html/rfc7518#section-6.2.1 - p := pub.Curve.Params() - n := p.BitSize / 8 - if p.BitSize%8 != 0 { - n++ - } - x := pub.X.Bytes() - if n > len(x) { - x = append(make([]byte, n-len(x)), x...) - } - y := pub.Y.Bytes() - if n > len(y) { - y = append(make([]byte, n-len(y)), y...) - } - // Field order is important. - // See https://tools.ietf.org/html/rfc7638#section-3.3 for details. - return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`, - p.Name, - base64.RawURLEncoding.EncodeToString(x), - base64.RawURLEncoding.EncodeToString(y), - ), nil - } - return "", ErrUnsupportedKey -} - -// jwsSign signs the digest using the given key. -// It returns ErrUnsupportedKey if the key type is unknown. -// The hash is used only for RSA keys. -func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) { - switch key := key.(type) { - case *rsa.PrivateKey: - return key.Sign(rand.Reader, digest, hash) - case *ecdsa.PrivateKey: - r, s, err := ecdsa.Sign(rand.Reader, key, digest) - if err != nil { - return nil, err - } - rb, sb := r.Bytes(), s.Bytes() - size := key.Params().BitSize / 8 - if size%8 > 0 { - size++ - } - sig := make([]byte, size*2) - copy(sig[size-len(rb):], rb) - copy(sig[size*2-len(sb):], sb) - return sig, nil - } - return nil, ErrUnsupportedKey -} - -// jwsHasher indicates suitable JWS algorithm name and a hash function -// to use for signing a digest with the provided key. -// It returns ("", 0) if the key is not supported. -func jwsHasher(key crypto.Signer) (string, crypto.Hash) { - switch key := key.(type) { - case *rsa.PrivateKey: - return "RS256", crypto.SHA256 - case *ecdsa.PrivateKey: - switch key.Params().Name { - case "P-256": - return "ES256", crypto.SHA256 - case "P-384": - return "ES384", crypto.SHA384 - case "P-521": - return "ES512", crypto.SHA512 - } - } - return "", 0 -} - -// JWKThumbprint creates a JWK thumbprint out of pub -// as specified in https://tools.ietf.org/html/rfc7638. -func JWKThumbprint(pub crypto.PublicKey) (string, error) { - jwk, err := jwkEncode(pub) - if err != nil { - return "", err - } - b := sha256.Sum256([]byte(jwk)) - return base64.RawURLEncoding.EncodeToString(b[:]), nil -} diff --git a/vendor/golang.org/x/crypto/acme/types.go b/vendor/golang.org/x/crypto/acme/types.go deleted file mode 100644 index 3e199749e..000000000 --- a/vendor/golang.org/x/crypto/acme/types.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package acme - -import ( - "crypto" - "crypto/x509" - "errors" - "fmt" - "net/http" - "strings" - "time" -) - -// ACME server response statuses used to describe Authorization and Challenge states. -const ( - StatusUnknown = "unknown" - StatusPending = "pending" - StatusProcessing = "processing" - StatusValid = "valid" - StatusInvalid = "invalid" - StatusRevoked = "revoked" -) - -// CRLReasonCode identifies the reason for a certificate revocation. -type CRLReasonCode int - -// CRL reason codes as defined in RFC 5280. -const ( - CRLReasonUnspecified CRLReasonCode = 0 - CRLReasonKeyCompromise CRLReasonCode = 1 - CRLReasonCACompromise CRLReasonCode = 2 - CRLReasonAffiliationChanged CRLReasonCode = 3 - CRLReasonSuperseded CRLReasonCode = 4 - CRLReasonCessationOfOperation CRLReasonCode = 5 - CRLReasonCertificateHold CRLReasonCode = 6 - CRLReasonRemoveFromCRL CRLReasonCode = 8 - CRLReasonPrivilegeWithdrawn CRLReasonCode = 9 - CRLReasonAACompromise CRLReasonCode = 10 -) - -// ErrUnsupportedKey is returned when an unsupported key type is encountered. -var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported") - -// Error is an ACME error, defined in Problem Details for HTTP APIs doc -// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem. -type Error struct { - // StatusCode is The HTTP status code generated by the origin server. - StatusCode int - // ProblemType is a URI reference that identifies the problem type, - // typically in a "urn:acme:error:xxx" form. - ProblemType string - // Detail is a human-readable explanation specific to this occurrence of the problem. - Detail string - // Header is the original server error response headers. - // It may be nil. - Header http.Header -} - -func (e *Error) Error() string { - return fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail) -} - -// AuthorizationError indicates that an authorization for an identifier -// did not succeed. -// It contains all errors from Challenge items of the failed Authorization. -type AuthorizationError struct { - // URI uniquely identifies the failed Authorization. - URI string - - // Identifier is an AuthzID.Value of the failed Authorization. - Identifier string - - // Errors is a collection of non-nil error values of Challenge items - // of the failed Authorization. - Errors []error -} - -func (a *AuthorizationError) Error() string { - e := make([]string, len(a.Errors)) - for i, err := range a.Errors { - e[i] = err.Error() - } - return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; ")) -} - -// RateLimit reports whether err represents a rate limit error and -// any Retry-After duration returned by the server. -// -// See the following for more details on rate limiting: -// https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-5.6 -func RateLimit(err error) (time.Duration, bool) { - e, ok := err.(*Error) - if !ok { - return 0, false - } - // Some CA implementations may return incorrect values. - // Use case-insensitive comparison. - if !strings.HasSuffix(strings.ToLower(e.ProblemType), ":ratelimited") { - return 0, false - } - if e.Header == nil { - return 0, true - } - return retryAfter(e.Header.Get("Retry-After"), 0), true -} - -// Account is a user account. It is associated with a private key. -type Account struct { - // URI is the account unique ID, which is also a URL used to retrieve - // account data from the CA. - URI string - - // Contact is a slice of contact info used during registration. - Contact []string - - // The terms user has agreed to. - // A value not matching CurrentTerms indicates that the user hasn't agreed - // to the actual Terms of Service of the CA. - AgreedTerms string - - // Actual terms of a CA. - CurrentTerms string - - // Authz is the authorization URL used to initiate a new authz flow. - Authz string - - // Authorizations is a URI from which a list of authorizations - // granted to this account can be fetched via a GET request. - Authorizations string - - // Certificates is a URI from which a list of certificates - // issued for this account can be fetched via a GET request. - Certificates string -} - -// Directory is ACME server discovery data. -type Directory struct { - // RegURL is an account endpoint URL, allowing for creating new - // and modifying existing accounts. - RegURL string - - // AuthzURL is used to initiate Identifier Authorization flow. - AuthzURL string - - // CertURL is a new certificate issuance endpoint URL. - CertURL string - - // RevokeURL is used to initiate a certificate revocation flow. - RevokeURL string - - // Term is a URI identifying the current terms of service. - Terms string - - // Website is an HTTP or HTTPS URL locating a website - // providing more information about the ACME server. - Website string - - // CAA consists of lowercase hostname elements, which the ACME server - // recognises as referring to itself for the purposes of CAA record validation - // as defined in RFC6844. - CAA []string -} - -// Challenge encodes a returned CA challenge. -// Its Error field may be non-nil if the challenge is part of an Authorization -// with StatusInvalid. -type Challenge struct { - // Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01". - Type string - - // URI is where a challenge response can be posted to. - URI string - - // Token is a random value that uniquely identifies the challenge. - Token string - - // Status identifies the status of this challenge. - Status string - - // Error indicates the reason for an authorization failure - // when this challenge was used. - // The type of a non-nil value is *Error. - Error error -} - -// Authorization encodes an authorization response. -type Authorization struct { - // URI uniquely identifies a authorization. - URI string - - // Status identifies the status of an authorization. - Status string - - // Identifier is what the account is authorized to represent. - Identifier AuthzID - - // Challenges that the client needs to fulfill in order to prove possession - // of the identifier (for pending authorizations). - // For final authorizations, the challenges that were used. - Challenges []*Challenge - - // A collection of sets of challenges, each of which would be sufficient - // to prove possession of the identifier. - // Clients must complete a set of challenges that covers at least one set. - // Challenges are identified by their indices in the challenges array. - // If this field is empty, the client needs to complete all challenges. - Combinations [][]int -} - -// AuthzID is an identifier that an account is authorized to represent. -type AuthzID struct { - Type string // The type of identifier, e.g. "dns". - Value string // The identifier itself, e.g. "example.org". -} - -// wireAuthz is ACME JSON representation of Authorization objects. -type wireAuthz struct { - Status string - Challenges []wireChallenge - Combinations [][]int - Identifier struct { - Type string - Value string - } -} - -func (z *wireAuthz) authorization(uri string) *Authorization { - a := &Authorization{ - URI: uri, - Status: z.Status, - Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value}, - Combinations: z.Combinations, // shallow copy - Challenges: make([]*Challenge, len(z.Challenges)), - } - for i, v := range z.Challenges { - a.Challenges[i] = v.challenge() - } - return a -} - -func (z *wireAuthz) error(uri string) *AuthorizationError { - err := &AuthorizationError{ - URI: uri, - Identifier: z.Identifier.Value, - } - for _, raw := range z.Challenges { - if raw.Error != nil { - err.Errors = append(err.Errors, raw.Error.error(nil)) - } - } - return err -} - -// wireChallenge is ACME JSON challenge representation. -type wireChallenge struct { - URI string `json:"uri"` - Type string - Token string - Status string - Error *wireError -} - -func (c *wireChallenge) challenge() *Challenge { - v := &Challenge{ - URI: c.URI, - Type: c.Type, - Token: c.Token, - Status: c.Status, - } - if v.Status == "" { - v.Status = StatusPending - } - if c.Error != nil { - v.Error = c.Error.error(nil) - } - return v -} - -// wireError is a subset of fields of the Problem Details object -// as described in https://tools.ietf.org/html/rfc7807#section-3.1. -type wireError struct { - Status int - Type string - Detail string -} - -func (e *wireError) error(h http.Header) *Error { - return &Error{ - StatusCode: e.Status, - ProblemType: e.Type, - Detail: e.Detail, - Header: h, - } -} - -// CertOption is an optional argument type for the TLSSNIxChallengeCert methods for -// customizing a temporary certificate for TLS-SNI challenges. -type CertOption interface { - privateCertOpt() -} - -// WithKey creates an option holding a private/public key pair. -// The private part signs a certificate, and the public part represents the signee. -func WithKey(key crypto.Signer) CertOption { - return &certOptKey{key} -} - -type certOptKey struct { - key crypto.Signer -} - -func (*certOptKey) privateCertOpt() {} - -// WithTemplate creates an option for specifying a certificate template. -// See x509.CreateCertificate for template usage details. -// -// In TLSSNIxChallengeCert methods, the template is also used as parent, -// resulting in a self-signed certificate. -// The DNSNames field of t is always overwritten for tls-sni challenge certs. -func WithTemplate(t *x509.Certificate) CertOption { - return (*certOptTemplate)(t) -} - -type certOptTemplate x509.Certificate - -func (*certOptTemplate) privateCertOpt() {}