Full Code of fabiolb/fabio for AI

master e7c17a6cfca8 cached
348 files
1.2 MB
484.6k tokens
892 symbols
1 requests
Download .txt
Showing preview only (1,291K chars total). Download the full file or copy to clipboard to get everything.
Repository: fabiolb/fabio
Branch: master
Commit: e7c17a6cfca8
Files: 348
Total size: 1.2 MB

Directory structure:
gitextract_kam7pb3_/

├── .dockerignore
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE.md
│   └── workflows/
│       ├── build.yml
│       ├── github-pages.yml
│       └── go-releaser.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── Dockerfile-goreleaser
├── LICENSE
├── Makefile
├── NOTICES.txt
├── README.md
├── admin/
│   ├── api/
│   │   ├── api.go
│   │   ├── config.go
│   │   ├── manual.go
│   │   ├── paths.go
│   │   ├── routes.go
│   │   └── version.go
│   ├── server.go
│   ├── server_test.go
│   └── ui/
│       ├── assets/
│       │   └── fonts/
│       │       └── material-icons.css
│       ├── generate.go
│       ├── manual.go
│       ├── route.go
│       └── static.go
├── assert/
│   └── assert.go
├── auth/
│   ├── auth.go
│   ├── auth_test.go
│   ├── basic.go
│   └── basic_test.go
├── bgp/
│   ├── bgp_nonwindows.go
│   ├── bgp_nonwindows_test.go
│   ├── bgp_windows.go
│   ├── logger.go
│   └── test_data/
│       └── bgp.toml
├── build/
│   ├── ca-certificates.crt
│   ├── homebrew.sh
│   ├── homebrew.vim
│   ├── issue-225-gen-cert.bash
│   ├── releasenotes.pl
│   ├── tag.sh
│   └── update-ssl.sh
├── cert/
│   ├── consul_source.go
│   ├── consul_source_test.go
│   ├── file_source.go
│   ├── http_source.go
│   ├── load.go
│   ├── load_test.go
│   ├── path_source.go
│   ├── source.go
│   ├── source_test.go
│   ├── store.go
│   ├── store_test.go
│   ├── vault_client.go
│   ├── vault_pki_source.go
│   ├── vault_source.go
│   └── watch.go
├── config/
│   ├── config.go
│   ├── default.go
│   ├── flagset.go
│   ├── flagset_test.go
│   ├── kvslice.go
│   ├── kvslice_test.go
│   ├── load.go
│   ├── load_test.go
│   └── localip.go
├── demo/
│   └── aws/
│       ├── .gitignore
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
├── docs/
│   ├── .gitignore
│   ├── README.md
│   ├── archetypes/
│   │   └── default.md
│   ├── config.toml
│   ├── content/
│   │   ├── _index.md
│   │   ├── cfg/
│   │   │   └── _index.md
│   │   ├── code-of-conduct/
│   │   │   └── _index.md
│   │   ├── contact/
│   │   │   └── _index.md
│   │   ├── contrib/
│   │   │   ├── _index.md
│   │   │   ├── development.md
│   │   │   └── guidelines.md
│   │   ├── deploy/
│   │   │   ├── _index.md
│   │   │   ├── amazon-api-gw.md
│   │   │   ├── amazon-elb.md
│   │   │   ├── direct.md
│   │   │   └── existing-lb.md
│   │   ├── faq/
│   │   │   ├── _index.md
│   │   │   ├── binding-to-low-ports.md
│   │   │   ├── multiple-protocol-listeners.md
│   │   │   ├── request-debugging.md
│   │   │   ├── verifying-releases.md
│   │   │   └── why-fabio.md
│   │   ├── feature/
│   │   │   ├── _index.md
│   │   │   ├── access-control.md
│   │   │   ├── access-logging.md
│   │   │   ├── authorization.md
│   │   │   ├── bgp.md
│   │   │   ├── certificate-stores.md
│   │   │   ├── docker.md
│   │   │   ├── dynamic-reloading.md
│   │   │   ├── graceful-shutdown.md
│   │   │   ├── grpc-proxy.md
│   │   │   ├── http-compression.md
│   │   │   ├── http-headers.md
│   │   │   ├── http-path-prepending.md
│   │   │   ├── http-path-stripping.md
│   │   │   ├── http-redirects.md
│   │   │   ├── https-tcp-sni-proxy.md
│   │   │   ├── https-upstream.md
│   │   │   ├── metrics.md
│   │   │   ├── proxy-protocol.md
│   │   │   ├── sse.md
│   │   │   ├── tcp-dynamic-proxy.md
│   │   │   ├── tcp-proxy.md
│   │   │   ├── tcp-sni-proxy.md
│   │   │   ├── traffic-shaping.md
│   │   │   ├── vault.md
│   │   │   ├── web-ui.md
│   │   │   └── websockets.md
│   │   ├── quickstart/
│   │   │   └── _index.md
│   │   └── ref/
│   │       ├── _index.md
│   │       ├── bgp.anycastaddresses.md
│   │       ├── bgp.asn.md
│   │       ├── bgp.certfile.md
│   │       ├── bgp.enabled.md
│   │       ├── bgp.enablegrpc.md
│   │       ├── bgp.gobgpdcfgfile.md
│   │       ├── bgp.grpclistenaddress.md
│   │       ├── bgp.grpctls.md
│   │       ├── bgp.keyfile.md
│   │       ├── bgp.listenaddresses.md
│   │       ├── bgp.listenport.md
│   │       ├── bgp.nexthop.md
│   │       ├── bgp.peers.md
│   │       ├── bgp.routerid.md
│   │       ├── glob.cache.size.md
│   │       ├── glob.matching.disabled.md
│   │       ├── log.access.format.md
│   │       ├── log.access.target.md
│   │       ├── log.level.md
│   │       ├── log.routes.format.md
│   │       ├── metrics.circonus.apiapp.md
│   │       ├── metrics.circonus.apikey.md
│   │       ├── metrics.circonus.apiurl.md
│   │       ├── metrics.circonus.brokerid.md
│   │       ├── metrics.circonus.checkid.md
│   │       ├── metrics.circonus.submissionurl.md
│   │       ├── metrics.dogstatsd.addr.md
│   │       ├── metrics.graphite.addr.md
│   │       ├── metrics.interval.md
│   │       ├── metrics.names.md
│   │       ├── metrics.prefix.md
│   │       ├── metrics.prometheus.buckets.md
│   │       ├── metrics.prometheus.path.md
│   │       ├── metrics.prometheus.subsystem.md
│   │       ├── metrics.retry.md
│   │       ├── metrics.statsd.addr.md
│   │       ├── metrics.target.md
│   │       ├── metrics.timeout.md
│   │       ├── proxy.addr.md
│   │       ├── proxy.auth.md
│   │       ├── proxy.cs.md
│   │       ├── proxy.deregistergraceperiod.md
│   │       ├── proxy.dialtimeout.md
│   │       ├── proxy.flushinterval.md
│   │       ├── proxy.globalflushinterval.md
│   │       ├── proxy.grpcmaxrxmsgsize.md
│   │       ├── proxy.grpcmaxtxmsgsize.md
│   │       ├── proxy.grpcshutdowntimeout.md
│   │       ├── proxy.gzip.contenttype.md
│   │       ├── proxy.header.clientip.md
│   │       ├── proxy.header.requestid.md
│   │       ├── proxy.header.sts.maxage.md
│   │       ├── proxy.header.sts.preload.md
│   │       ├── proxy.header.sts.subdomains.md
│   │       ├── proxy.header.tls.md
│   │       ├── proxy.header.tls.value.md
│   │       ├── proxy.idleconntimeout.md
│   │       ├── proxy.keepalivetimeout.md
│   │       ├── proxy.localip.md
│   │       ├── proxy.matcher.md
│   │       ├── proxy.maxconn.md
│   │       ├── proxy.noroutestatus.md
│   │       ├── proxy.responseheadertimeout.md
│   │       ├── proxy.shutdownwait.md
│   │       ├── proxy.strategy.md
│   │       ├── registry.backend.md
│   │       ├── registry.consul.addr.md
│   │       ├── registry.consul.checksRequired.md
│   │       ├── registry.consul.kvpath.md
│   │       ├── registry.consul.namespace.md
│   │       ├── registry.consul.noroutehtmlpath.md
│   │       ├── registry.consul.pollInterval.md
│   │       ├── registry.consul.register.addr.md
│   │       ├── registry.consul.register.checkInterval.md
│   │       ├── registry.consul.register.checkTLSSkipVerify.md
│   │       ├── registry.consul.register.checkTimeout.md
│   │       ├── registry.consul.register.deregisterCriticalServiceAfter.md
│   │       ├── registry.consul.register.enabled.md
│   │       ├── registry.consul.register.name.md
│   │       ├── registry.consul.register.tags.md
│   │       ├── registry.consul.service.status.md
│   │       ├── registry.consul.serviceMonitors.md
│   │       ├── registry.consul.tagprefix.md
│   │       ├── registry.consul.token.md
│   │       ├── registry.custom.checkTLSSkipVerify.md
│   │       ├── registry.custom.host.md
│   │       ├── registry.custom.path.md
│   │       ├── registry.custom.pollinginterval.md
│   │       ├── registry.custom.queryparams.md
│   │       ├── registry.custom.scheme.md
│   │       ├── registry.custom.timeout.md
│   │       ├── registry.file.noroutehtmlpath.md
│   │       ├── registry.file.path.md
│   │       ├── registry.retry.md
│   │       ├── registry.static.noroutehtmlpath.md
│   │       ├── registry.static.routes.md
│   │       ├── registry.timeout.md
│   │       ├── runtime.gogc.md
│   │       ├── runtime.gomaxprocs.md
│   │       ├── ui.access.md
│   │       ├── ui.addr.md
│   │       ├── ui.color.md
│   │       ├── ui.routingtable.source.host.md
│   │       ├── ui.routingtable.source.linkenabled.md
│   │       ├── ui.routingtable.source.newtab.md
│   │       ├── ui.routingtable.source.port.md
│   │       ├── ui.routingtable.source.scheme.md
│   │       └── ui.title.md
│   ├── layouts/
│   │   ├── 404.html
│   │   ├── _default/
│   │   │   ├── section.html
│   │   │   └── single.html
│   │   ├── index.html
│   │   └── partials/
│   │       ├── footer.html
│   │       ├── header.html
│   │       └── sidebar.html
│   └── static/
│       ├── CNAME
│       ├── check/
│       │   └── ok
│       ├── css/
│       │   └── custom.css
│       ├── fonts/
│       │   └── FontAwesome.otf
│       ├── img/
│       │   └── manifest.json
│       └── js/
│           └── custom.js
├── exit/
│   ├── listen.go
│   └── listen_test.go
├── fabio.iml
├── fabio.properties
├── go.mod
├── go.sum
├── logger/
│   ├── level_writer.go
│   ├── level_writer_test.go
│   ├── logger.go
│   ├── logger_test.go
│   └── pattern.go
├── main.go
├── metrics/
│   ├── metrics.go
│   ├── names.go
│   ├── names_test.go
│   ├── provider_circonus.go
│   ├── provider_circonus_test.go
│   ├── provider_discard.go
│   ├── provider_dogstatsd.go
│   ├── provider_dogstatsd_test.go
│   ├── provider_flat.go
│   ├── provider_graphite.go
│   ├── provider_label.go
│   ├── provider_multi.go
│   ├── provider_prometheus.go
│   ├── provider_prometheus_test.go
│   ├── provider_statsd.go
│   └── provider_statsd_test.go
├── noroute/
│   ├── store.go
│   └── store_test.go
├── proxy/
│   ├── grpc_handler.go
│   ├── gzip/
│   │   ├── content_type_test.go
│   │   ├── gzip_handler.go
│   │   └── gzip_handler_test.go
│   ├── http_handler.go
│   ├── http_headers.go
│   ├── http_headers_test.go
│   ├── http_integration_test.go
│   ├── http_proxy.go
│   ├── inetaf_tcpproxy.go
│   ├── inetaf_tcpproxy_integration_test.go
│   ├── internal/
│   │   └── testcert.go
│   ├── listen.go
│   ├── listen_test.go
│   ├── serve.go
│   ├── tcp/
│   │   ├── copy_buffer.go
│   │   ├── proxy_proto.go
│   │   ├── server.go
│   │   ├── sni_proxy.go
│   │   ├── tcp_dynamic_proxy.go
│   │   ├── tcp_proxy.go
│   │   ├── tcptest/
│   │   │   ├── dialer.go
│   │   │   └── server.go
│   │   ├── tls_clienthello.go
│   │   └── tls_clienthello_test.go
│   ├── tcp_integration_test.go
│   ├── ws_handler.go
│   └── ws_integration_test.go
├── registry/
│   ├── backend.go
│   ├── consul/
│   │   ├── backend.go
│   │   ├── kv.go
│   │   ├── passing.go
│   │   ├── passing_test.go
│   │   ├── register.go
│   │   ├── routecmd.go
│   │   ├── routecmd_test.go
│   │   └── service.go
│   ├── custom/
│   │   ├── backend.go
│   │   ├── custom.go
│   │   └── custom_test.go
│   ├── file/
│   │   └── backend.go
│   └── static/
│       └── backend.go
├── rootwarn_unix.go
├── rootwarn_windows.go
├── route/
│   ├── access_rules.go
│   ├── access_rules_test.go
│   ├── auth.go
│   ├── auth_test.go
│   ├── glob_cache.go
│   ├── glob_cache_test.go
│   ├── issue57_test.go
│   ├── matcher.go
│   ├── matcher_test.go
│   ├── metrics_cleanup_test.go
│   ├── parse_new.go
│   ├── parse_test.go
│   ├── picker.go
│   ├── picker_test.go
│   ├── route.go
│   ├── route_bench_test.go
│   ├── route_def.go
│   ├── routes.go
│   ├── table.go
│   ├── table_registry_test.go
│   ├── table_test.go
│   ├── target.go
│   └── target_test.go
├── transport/
│   └── transport.go
└── uuid/
    ├── format.go
    └── uuid.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
fabio
dist/


================================================
FILE: .github/CODEOWNERS
================================================
* @fabiolb/maintainers


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================


================================================
FILE: .github/workflows/build.yml
================================================
on: [push, pull_request]
name: Build
permissions:
  contents: read
jobs:
  golangci:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version-file: 'go.mod'
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v8
        with:
          version: v2.5.0
  build:
    runs-on: ubuntu-latest
    needs: ["golangci"]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version-file: 'go.mod'
          check-latest: true
          cache: true
      - name: Set Hosts
        run: |
          echo "127.0.0.1	example.com example2.com" | sudo tee -a /etc/hosts
      - name: Test
        run: |
          export PATH=$PATH:$HOME/bin:$HOME/go/bin
          make github


================================================
FILE: .github/workflows/github-pages.yml
================================================
name: github pages
permissions:
  contents: write
on:
  push:
    branches:
      - master
jobs:
  build-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true
      - name: build
        run: |
          export PATH=${PATH}:${HOME}/bin
          make github-pages
      - name: deploy
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs/public
          cname: fabiolb.net


================================================
FILE: .github/workflows/go-releaser.yml
================================================
name: goreleaser
permissions:
  contents: write
on:
  push:
    tags:
      - '*'
jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version-file: 'go.mod'
          check-latest: true
          cache: true
      - name: Docker Login
        uses: docker/login-action@v3
        with:
          registry: https://index.docker.io/v1/
          username: ${{ secrets.DOCKER_HUB_USER }}
          password: ${{ secrets.DOCKER_HUB_TOKEN }}
      - name: Import GPG key
        id: import_gpg
        uses: crazy-max/ghaction-import-gpg@v6
        with:
          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
          passphrase: ${{ secrets.GPG_PASSPHRASE }}
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6
        with:
          distribution: goreleaser
          version: '~> v2'
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}


================================================
FILE: .gitignore
================================================
*-amd64
*.orig
*.out
*.p12
*.pem
*.pprof
*.sha256
*.swp
*.tar.gz
*.test
*.un~
*.zip
.DS_Store
.idea
.vagrant
build/builds/
fabio
fabio.exe
fabio.sublime-*
demo/cert/
/pkg/
dist/
*.app
*.hugo_build.lock
*~
.RELEASE.CHANGELOG.md


================================================
FILE: .golangci.yml
================================================
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.

version: "2"
run:
  concurrency: 4
  allow-parallel-runners: true
  allow-serial-runners: true
linters:
  default: all
  disable:
    - bodyclose
    - containedctx
    - copyloopvar
    - cyclop
    - depguard
    - dupl
    - dupword
    - embeddedstructfieldcheck
    - err113
    - errcheck
    - errchkjson
    - errorlint
    - exhaustive
    - exhaustruct
    - forbidigo
    - forcetypeassert
    - funcorder
    - funlen
    - gochecknoglobals
    - gochecknoinits
    - gocognit
    - goconst
    - gocritic
    - gocyclo
    - godoclint
    - godot
    - godox
    - gosec
    - govet
    - ireturn
    - lll
    - maintidx
    - mnd
    - nakedret
    - nestif
    - nilnil
    - nlreturn
    - noctx
    - noinlineerr
    - nonamedreturns
    - paralleltest
    - perfsprint
    - prealloc
    - protogetter
    - revive
    - tagliatelle
    - testpackage
    - thelper
    - usestdlibvars
    - usetesting
    - varnamelen
    - whitespace
    - wrapcheck
    - wsl
    - wsl_v5
  exclusions:
    generated: lax
    presets:
      - comments
      - common-false-positives
      - legacy
      - std-error-handling
    paths:
      - demo/*
formatters:
  exclusions:
    generated: lax


================================================
FILE: .goreleaser.yml
================================================
version: 2
builds:
  - binary: fabio
    env:
      - CGO_ENABLED=0
    goos:
      - darwin
      - linux
      - freebsd
      - netbsd
      - openbsd
      - windows
    goarch:
      - 386
      - amd64
      - arm
      - arm64
    goarm:
      - 7
archives:
  - id: bin
    name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}_{{ .Arch }}'
    formats:
      - binary
source:
  enabled: true
  name_template: '{{ .ProjectName }}-{{.Version }}.src'
  prefix_template: '{{ .ProjectName }}-{{.Version }}/'
checksum:
  name_template: '{{.ProjectName}}-{{.Version}}.sha256'
signs:
  - artifacts: checksum
    args:
      - "--batch"
      - "--local-user"
      - "{{ .Env.GPG_FINGERPRINT }}"
      - "--output"
      - "${signature}"
      - "--detach-sign"
      - "${artifact}"
dockers:
  - dockerfile: Dockerfile-goreleaser
    use: buildx
    goos: linux
    goarch: amd64
    image_templates:
      - 'fabiolb/fabio:latest-amd64'
      - 'fabiolb/fabio:{{ .Version }}-amd64'
    build_flag_templates:
      - "--platform=linux/amd64"
      - "--label=org.opencontainers.image.created={{.Date}}"
      - "--label=org.opencontainers.image.title={{.ProjectName}}"
      - "--label=org.opencontainers.image.revision={{.FullCommit}}"
      - "--label=org.opencontainers.image.version={{.Version}}"
    extra_files:
      - fabio.properties
  - dockerfile: Dockerfile-goreleaser
    use: buildx
    goos: linux
    goarch: arm64
    image_templates:
      - 'fabiolb/fabio:latest-arm64v8'
      - 'fabiolb/fabio:{{ .Version }}-arm64v8'
    build_flag_templates:
      - "--platform=linux/arm64/v8"
      - "--label=org.opencontainers.image.created={{.Date}}"
      - "--label=org.opencontainers.image.title={{.ProjectName}}"
      - "--label=org.opencontainers.image.revision={{.FullCommit}}"
      - "--label=org.opencontainers.image.version={{.Version}}"
    extra_files:
      - fabio.properties
  - dockerfile: Dockerfile-goreleaser
    use: buildx
    goos: linux
    goarch: arm
    goarm: 7
    image_templates:
      - 'fabiolb/fabio:latest-armv7'
      - 'fabiolb/fabio:{{ .Version }}-armv7'
    build_flag_templates:
      - "--platform=linux/arm/v7"
      - "--label=org.opencontainers.image.created={{.Date}}"
      - "--label=org.opencontainers.image.title={{.ProjectName}}"
      - "--label=org.opencontainers.image.revision={{.FullCommit}}"
      - "--label=org.opencontainers.image.version={{.Version}}"
    extra_files:
      - fabio.properties
docker_manifests:
  - name_template: 'fabiolb/fabio:latest'
    image_templates:
      - 'fabiolb/fabio:latest-amd64'
      - 'fabiolb/fabio:latest-arm64v8'
      - 'fabiolb/fabio:latest-armv7'
  - name_template: 'fabiolb/fabio:{{ .Version }}'
    image_templates:
      - 'fabiolb/fabio:{{ .Version }}-amd64'
      - 'fabiolb/fabio:{{ .Version }}-arm64v8'
      - 'fabiolb/fabio:{{ .Version }}-armv7'


================================================
FILE: CHANGELOG.md
================================================
# Changelog

## [v1.6.10](https://github.com/fabiolb/fabio/tree/v1.6.10) (2025-11-24)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.9...v1.6.10)

**Closed issues:**

- Call the fatal function within the goroutine of the main test function [\#1009](https://github.com/fabiolb/fabio/issues/1009)
- Support for authentication middleware \(eg: oauth2-proxy\) ? [\#1006](https://github.com/fabiolb/fabio/issues/1006)

**Merged pull requests:**

- Update deps for upstream fixes. [\#1011](https://github.com/fabiolb/fabio/pull/1011) ([tristanmorgan](https://github.com/tristanmorgan))
- Reverse IPv4/IPv6 check. [\#1010](https://github.com/fabiolb/fabio/pull/1010) ([tristanmorgan](https://github.com/tristanmorgan))

## [v1.6.9](https://github.com/fabiolb/fabio/tree/v1.6.9) (2025-10-16)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.8...v1.6.9)

**Merged pull requests:**

- FROM scratch build and update deps. [\#1008](https://github.com/fabiolb/fabio/pull/1008) ([tristanmorgan](https://github.com/tristanmorgan))
- feat: add armv7 support for docker images [\#1007](https://github.com/fabiolb/fabio/pull/1007) ([amd989](https://github.com/amd989))

## [v1.6.8](https://github.com/fabiolb/fabio/tree/v1.6.8) (2025-09-23)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.7...v1.6.8)

**Closed issues:**

- Health check on port 9999 [\#995](https://github.com/fabiolb/fabio/issues/995)
- Multiple entries in proxy.auth do not work as specified in documentation [\#929](https://github.com/fabiolb/fabio/issues/929)
- Correct Consul ACL Policy for Fabio [\#831](https://github.com/fabiolb/fabio/issues/831)
- Translating `upstream-host/path` to `path.example.com` [\#801](https://github.com/fabiolb/fabio/issues/801)

**Merged pull requests:**

- Package dependencies updated. [\#1004](https://github.com/fabiolb/fabio/pull/1004) ([tristanmorgan](https://github.com/tristanmorgan))
- docs: Fixes bad example for creating multiple basic authorization schemes [\#1003](https://github.com/fabiolb/fabio/pull/1003) ([steffkelsey](https://github.com/steffkelsey))
- Enabling many more linters in the pipeline. [\#999](https://github.com/fabiolb/fabio/pull/999) ([tristanmorgan](https://github.com/tristanmorgan))
- Fix missing brand logo in routes page [\#998](https://github.com/fabiolb/fabio/pull/998) ([tristanmorgan](https://github.com/tristanmorgan))
- Fixing up some web links [\#997](https://github.com/fabiolb/fabio/pull/997) ([tristanmorgan](https://github.com/tristanmorgan))
- Fix the ui.routingtable.source.newtab doc. [\#996](https://github.com/fabiolb/fabio/pull/996) ([tristanmorgan](https://github.com/tristanmorgan))
- extract only binary from zipfile [\#994](https://github.com/fabiolb/fabio/pull/994) ([shantanugadgil](https://github.com/shantanugadgil))
- add option to constrain fabio instance to specific consul namespace [\#812](https://github.com/fabiolb/fabio/pull/812) ([baabgai](https://github.com/baabgai))

## [v1.6.7](https://github.com/fabiolb/fabio/tree/v1.6.7) (2025-05-30)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.6...v1.6.7)

**Merged pull requests:**

- Bump go.mod pins to latest point release. [\#993](https://github.com/fabiolb/fabio/pull/993) ([tristanmorgan](https://github.com/tristanmorgan))

## [v1.6.6](https://github.com/fabiolb/fabio/tree/v1.6.6) (2025-05-26)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.5...v1.6.6)

**Implemented enhancements:**

- Add a golang-linter to CI [\#972](https://github.com/fabiolb/fabio/pull/972) ([aleksraiden](https://github.com/aleksraiden))

**Closed issues:**

- wiki content vs fabio/docs/content ? [\#986](https://github.com/fabiolb/fabio/issues/986)

**Merged pull requests:**

- Using staticcheck to fix many issues. [\#992](https://github.com/fabiolb/fabio/pull/992) ([tristanmorgan](https://github.com/tristanmorgan))
- Update golangci-lint and run yamlfmt. [\#990](https://github.com/fabiolb/fabio/pull/990) ([tristanmorgan](https://github.com/tristanmorgan))
- Fix mistake made in \#988. [\#989](https://github.com/fabiolb/fabio/pull/989) ([tristanmorgan](https://github.com/tristanmorgan))
- Actions permissions [\#988](https://github.com/fabiolb/fabio/pull/988) ([tristanmorgan](https://github.com/tristanmorgan))
- docs: fix broken link [\#987](https://github.com/fabiolb/fabio/pull/987) ([marco-m](https://github.com/marco-m))
- Update dependancies including GoBGP. [\#985](https://github.com/fabiolb/fabio/pull/985) ([tristanmorgan](https://github.com/tristanmorgan))
- Add a CODEOWNERS file. [\#983](https://github.com/fabiolb/fabio/pull/983) ([tristanmorgan](https://github.com/tristanmorgan))
- Document insensitive prefix matching in the list. [\#982](https://github.com/fabiolb/fabio/pull/982) ([tristanmorgan](https://github.com/tristanmorgan))
- Update golang.org/x/net. [\#980](https://github.com/fabiolb/fabio/pull/980) ([tristanmorgan](https://github.com/tristanmorgan))

## [v1.6.5](https://github.com/fabiolb/fabio/tree/v1.6.5) (2025-02-28)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.4...v1.6.5)

**Implemented enhancements:**

- Unable to load correct certificates if 1 invalid one is in consul k/v [\#941](https://github.com/fabiolb/fabio/issues/941)
- Use a Go 1.24.0 [\#971](https://github.com/fabiolb/fabio/pull/971) ([aleksraiden](https://github.com/aleksraiden))
- Report all certificate errors instead of stopping at the first. \(\#941\) [\#964](https://github.com/fabiolb/fabio/pull/964) ([tristanmorgan](https://github.com/tristanmorgan))

**Closed issues:**

- Please bump golang.org/x/sys dependency to enable a build on riscv64-freebsd [\#927](https://github.com/fabiolb/fabio/issues/927)
- Fabio is using Datadog reserved tag keys  [\#923](https://github.com/fabiolb/fabio/issues/923)

**Merged pull requests:**

- Update deps to latest [\#975](https://github.com/fabiolb/fabio/pull/975) ([aleksraiden](https://github.com/aleksraiden))
- updating godeps [\#969](https://github.com/fabiolb/fabio/pull/969) ([aleksraiden](https://github.com/aleksraiden))
- Update Hugo config to work with version bump in \#965 [\#967](https://github.com/fabiolb/fabio/pull/967) ([tristanmorgan](https://github.com/tristanmorgan))
- Use Alpine3.21 as base docker image [\#966](https://github.com/fabiolb/fabio/pull/966) ([aleksraiden](https://github.com/aleksraiden))
- update CI components [\#965](https://github.com/fabiolb/fabio/pull/965) ([aleksraiden](https://github.com/aleksraiden))
- README: remove mention to www.kijiji.it \(decommissioned in 2022\) [\#963](https://github.com/fabiolb/fabio/pull/963) ([marco-m-pix4d](https://github.com/marco-m-pix4d))
- Update dependancies again. [\#962](https://github.com/fabiolb/fabio/pull/962) ([tristanmorgan](https://github.com/tristanmorgan))
- Use ParseUint to test for overflow directly [\#961](https://github.com/fabiolb/fabio/pull/961) ([dcarbone](https://github.com/dcarbone))
- Fix small typo in DogStatsD config ref. [\#960](https://github.com/fabiolb/fabio/pull/960) ([tristanmorgan](https://github.com/tristanmorgan))
- Rebuild CHANGELOG [\#959](https://github.com/fabiolb/fabio/pull/959) ([tristanmorgan](https://github.com/tristanmorgan))
- Adds handling of Datadog reserved tag keys [\#924](https://github.com/fabiolb/fabio/pull/924) ([froque](https://github.com/froque))

## [v1.6.4](https://github.com/fabiolb/fabio/tree/v1.6.4) (2024-11-27)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.3...v1.6.4)

**Closed issues:**

- CI pipeline to run testsuite [\#949](https://github.com/fabiolb/fabio/issues/949)
- Fabio not exporting all the metrics with prometheus [\#947](https://github.com/fabiolb/fabio/issues/947)
- certificates - cert and ca chain/intermediate [\#946](https://github.com/fabiolb/fabio/issues/946)
- This repository is unmaintaned. [\#944](https://github.com/fabiolb/fabio/issues/944)
- CVE-2023-44487 HTTP/2 rapid reset [\#939](https://github.com/fabiolb/fabio/issues/939)
- TCP no route - cant balance tcp [\#936](https://github.com/fabiolb/fabio/issues/936)
- windows: setting logging path in fabio properties [\#920](https://github.com/fabiolb/fabio/issues/920)
- Port range in the proxy.addr [\#529](https://github.com/fabiolb/fabio/issues/529)

**Merged pull requests:**

- Add GoReleaser workflow. [\#958](https://github.com/fabiolb/fabio/pull/958) ([tristanmorgan](https://github.com/tristanmorgan))
- go-kit/kit/log go-kit/log [\#956](https://github.com/fabiolb/fabio/pull/956) ([tristanmorgan](https://github.com/tristanmorgan))
- Update go-retryablehttp to fix warning. [\#954](https://github.com/fabiolb/fabio/pull/954) ([tristanmorgan](https://github.com/tristanmorgan))
- Update and try fix GH Pages publish action. [\#953](https://github.com/fabiolb/fabio/pull/953) ([tristanmorgan](https://github.com/tristanmorgan))
- Update go version, test binaries and package versions. [\#952](https://github.com/fabiolb/fabio/pull/952) ([tristanmorgan](https://github.com/tristanmorgan))
- Remove vendored modules in favour of go mod. [\#951](https://github.com/fabiolb/fabio/pull/951) ([tristanmorgan](https://github.com/tristanmorgan))
- Update Github runner image. [\#950](https://github.com/fabiolb/fabio/pull/950) ([tristanmorgan](https://github.com/tristanmorgan))
- fix: close resp body [\#945](https://github.com/fabiolb/fabio/pull/945) ([testwill](https://github.com/testwill))
- Trim leading and trailing spaces from service tags [\#943](https://github.com/fabiolb/fabio/pull/943) ([logocomune](https://github.com/logocomune))
- Fix doubled download a Vault file [\#942](https://github.com/fabiolb/fabio/pull/942) ([aleksraiden](https://github.com/aleksraiden))
- Remove deprecated ioutil [\#940](https://github.com/fabiolb/fabio/pull/940) ([tristanmorgan](https://github.com/tristanmorgan))
- Dockerfile: add CAP\_NET\_BIND\_SERVICE+eip to fabio to allow running as root [\#938](https://github.com/fabiolb/fabio/pull/938) ([Kamilcuk](https://github.com/Kamilcuk))
- Consul registry performance improvements [\#928](https://github.com/fabiolb/fabio/pull/928) ([ddreier](https://github.com/ddreier))
- \[Docs\] Fix wrong parameter name [\#914](https://github.com/fabiolb/fabio/pull/914) ([KEANO89](https://github.com/KEANO89))
- Updating grpc handler to gracefully close backend connections [\#913](https://github.com/fabiolb/fabio/pull/913) ([nathanejohnson](https://github.com/nathanejohnson))

## [v1.6.3](https://github.com/fabiolb/fabio/tree/v1.6.3) (2022-12-09)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.2...v1.6.3)

**Implemented enhancements:**

- Feature request: Make source links in ui interface clickable [\#901](https://github.com/fabiolb/fabio/issues/901)

**Closed issues:**

- Ignore host=dst when backend is https [\#916](https://github.com/fabiolb/fabio/issues/916)
- poll new feature requests [\#910](https://github.com/fabiolb/fabio/issues/910)
- Fabio Clustering. [\#668](https://github.com/fabiolb/fabio/issues/668)

**Merged pull requests:**

- Disable BGP functionality on Windows since gobgp does not support this. [\#919](https://github.com/fabiolb/fabio/pull/919) ([nathanejohnson](https://github.com/nathanejohnson))
- updating CHANGELOG [\#918](https://github.com/fabiolb/fabio/pull/918) ([nathanejohnson](https://github.com/nathanejohnson))
- Don't use "dst" literal as sni name when host=dst is specified on https backends [\#917](https://github.com/fabiolb/fabio/pull/917) ([nathanejohnson](https://github.com/nathanejohnson))
- add feature to advertise anycast addresses via BGP [\#909](https://github.com/fabiolb/fabio/pull/909) ([nathanejohnson](https://github.com/nathanejohnson))
- Change the shutdown procedure to deregister fabio from the registry and then shutdown the proxy [\#908](https://github.com/fabiolb/fabio/pull/908) ([martinivanov](https://github.com/martinivanov))
- Feature/source link [\#907](https://github.com/fabiolb/fabio/pull/907) ([KTruesdellENA](https://github.com/KTruesdellENA))

## [v1.6.2](https://github.com/fabiolb/fabio/tree/v1.6.2) (2022-09-13)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.1...v1.6.2)

**Closed issues:**

- Update TLS cipher parser to include modern ciphers [\#903](https://github.com/fabiolb/fabio/issues/903)
- Custom behavior for the situation when the service has no healthy instances [\#898](https://github.com/fabiolb/fabio/issues/898)

**Merged pull requests:**

- update README for v1.6.2 release [\#905](https://github.com/fabiolb/fabio/pull/905) ([nathanejohnson](https://github.com/nathanejohnson))
- Updating TLS cipher config parser to include TLS 1.3 constants. [\#904](https://github.com/fabiolb/fabio/pull/904) ([nathanejohnson](https://github.com/nathanejohnson))

## [v1.6.1](https://github.com/fabiolb/fabio/tree/v1.6.1) (2022-07-19)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.6.0...v1.6.1)

**Implemented enhancements:**

- Multi-DC fabio [\#115](https://github.com/fabiolb/fabio/issues/115)

**Fixed bugs:**

- Crash: invalid log msg: http2: panic serving CLIENT\_IP:CLIENT\_PORT: runtime error: index out of range \[-1\] [\#872](https://github.com/fabiolb/fabio/issues/872)

**Closed issues:**

- admin UI Overrides not working [\#886](https://github.com/fabiolb/fabio/issues/886)
- Panic on created prometheus metric name [\#878](https://github.com/fabiolb/fabio/issues/878)
- Crash on route update: panic: runtime error: index out of range, diffmatchpatch.\(\*DiffMatchPatch\).DiffCharsToLines [\#873](https://github.com/fabiolb/fabio/issues/873)
- Experiencing 502's [\#862](https://github.com/fabiolb/fabio/issues/862)
- Fabio immediately drop routes when consul agent unavailable for a while [\#861](https://github.com/fabiolb/fabio/issues/861)
- \[proxy/tls\] Update supported TLS versions and cipher suites [\#858](https://github.com/fabiolb/fabio/issues/858)
- JSON schema is incorrect in website Dest should be Dst [\#852](https://github.com/fabiolb/fabio/issues/852)
- \[question\] URL for TLS destination [\#850](https://github.com/fabiolb/fabio/issues/850)
- \[Feature\] Possibility of adding arm/arm64 docker builds. [\#833](https://github.com/fabiolb/fabio/issues/833)

**Merged pull requests:**

- Release/v1.6.1 [\#897](https://github.com/fabiolb/fabio/pull/897) ([nathanejohnson](https://github.com/nathanejohnson))
- setting sni to match host [\#896](https://github.com/fabiolb/fabio/pull/896) ([KTruesdellENA](https://github.com/KTruesdellENA))
- Update random picker to use math/rand's Intn function [\#893](https://github.com/fabiolb/fabio/pull/893) ([nathanejohnson](https://github.com/nathanejohnson))
- add configurable grpc message sizes to \#632 [\#890](https://github.com/fabiolb/fabio/pull/890) ([nathanejohnson](https://github.com/nathanejohnson))
- add tls13 [\#889](https://github.com/fabiolb/fabio/pull/889) ([nathanejohnson](https://github.com/nathanejohnson))
- update materialize bits. see issue \#886 [\#888](https://github.com/fabiolb/fabio/pull/888) ([nathanejohnson](https://github.com/nathanejohnson))
- Moved admin UI assets to use go embed [\#885](https://github.com/fabiolb/fabio/pull/885) ([nathanejohnson](https://github.com/nathanejohnson))
- update the custom css [\#884](https://github.com/fabiolb/fabio/pull/884) ([KTruesdellENA](https://github.com/KTruesdellENA))
- Bump go-diff dependency version to 1.2.0.  Fixes \#873 [\#881](https://github.com/fabiolb/fabio/pull/881) ([nathanejohnson](https://github.com/nathanejohnson))
- bump HUGO version to 0.101.0 [\#880](https://github.com/fabiolb/fabio/pull/880) ([nathanejohnson](https://github.com/nathanejohnson))
- add docs from PR \#854 to fabio.properties [\#879](https://github.com/fabiolb/fabio/pull/879) ([nathanejohnson](https://github.com/nathanejohnson))
- Build multi-arch Docker images for amd64 and arm64 architectures [\#875](https://github.com/fabiolb/fabio/pull/875) ([vamc19](https://github.com/vamc19))
- Fix x-forwarded-for header processing for ws connections [\#860](https://github.com/fabiolb/fabio/pull/860) ([bn0ir](https://github.com/bn0ir))
- Update registry.backend.md [\#854](https://github.com/fabiolb/fabio/pull/854) ([webmutation](https://github.com/webmutation))
- Resulting complete routing table has no 'tags "a,b"'  in last line [\#841](https://github.com/fabiolb/fabio/pull/841) ([hb9cwp](https://github.com/hb9cwp))
- fixes \#807 - changes map for grpc connections to be a string key [\#816](https://github.com/fabiolb/fabio/pull/816) ([nathanejohnson](https://github.com/nathanejohnson))
- Add command line flag to toggle required consistency on consul reads [\#811](https://github.com/fabiolb/fabio/pull/811) ([jeremycw](https://github.com/jeremycw))
- Issue 605 grpc host matching [\#632](https://github.com/fabiolb/fabio/pull/632) ([tommyalatalo](https://github.com/tommyalatalo))

## [v1.6.0](https://github.com/fabiolb/fabio/tree/v1.6.0) (2022-04-11)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.15...v1.6.0)

**Implemented enhancements:**

- Add support for influxdb metrics [\#253](https://github.com/fabiolb/fabio/issues/253)
- Support for prometheus [\#211](https://github.com/fabiolb/fabio/issues/211)
- Support dogstatd with tags [\#165](https://github.com/fabiolb/fabio/issues/165)
- Riemann metrics support [\#126](https://github.com/fabiolb/fabio/issues/126)
- Simple HTTP path prefix replacement [\#767](https://github.com/fabiolb/fabio/pull/767) ([JamesJJ](https://github.com/JamesJJ))

**Closed issues:**

- Consul Route updates very slow with large numbers of routes [\#865](https://github.com/fabiolb/fabio/issues/865)
- Restricting TLS versions [\#859](https://github.com/fabiolb/fabio/issues/859)
- \[admin/ui\] General updates [\#856](https://github.com/fabiolb/fabio/issues/856)
- \[question\] - Can Fabio listen on 80/tcp with Nomad [\#844](https://github.com/fabiolb/fabio/issues/844)
- Supporting requests in the form of /my-app/page1 [\#842](https://github.com/fabiolb/fabio/issues/842)
- Fabio Using Container IPs to create routes [\#839](https://github.com/fabiolb/fabio/issues/839)
- All my dynamic routes suddenly vanished! [\#837](https://github.com/fabiolb/fabio/issues/837)
- Fabio redirecting to /routes on own service [\#832](https://github.com/fabiolb/fabio/issues/832)
- docu fabio configure TLS/SSL\(HTTPS\) understanding problem [\#827](https://github.com/fabiolb/fabio/issues/827)
- Crash: \[FATAL\] 1.5.13. strconv.ParseUint: parsing ":1883;PROTO=TCP-DYNAMIC": invalid syntax [\#826](https://github.com/fabiolb/fabio/issues/826)
- strip doesn't work as expected on redirect [\#824](https://github.com/fabiolb/fabio/issues/824)
- Using Fabio with Consul over mTLS [\#820](https://github.com/fabiolb/fabio/issues/820)
- Switch to github actions [\#817](https://github.com/fabiolb/fabio/issues/817)
- Panic - httputil: ReverseProxy read error during body copy [\#814](https://github.com/fabiolb/fabio/issues/814)
- Support for Consul and Vault Namespaces [\#810](https://github.com/fabiolb/fabio/issues/810)
- grpc be closed when uninstall service target [\#807](https://github.com/fabiolb/fabio/issues/807)
- fabio binary filename for download [\#805](https://github.com/fabiolb/fabio/issues/805)
- Add arm64 arch [\#804](https://github.com/fabiolb/fabio/issues/804)
- Can Fabio to prefer one ethernet interface for proxy\_addr? [\#802](https://github.com/fabiolb/fabio/issues/802)
- TCP Dynamic Proxy route without specifying exact IP? [\#797](https://github.com/fabiolb/fabio/issues/797)
- \[Question\] What are opinions on allowing stale reads of Consul Catalog [\#764](https://github.com/fabiolb/fabio/issues/764)
- Simple HTTP path prefix replacement [\#691](https://github.com/fabiolb/fabio/issues/691)
- Does Fabio support multiple CS Stores per listener? [\#666](https://github.com/fabiolb/fabio/issues/666)
- \[Question\] Stats - Status code per service [\#371](https://github.com/fabiolb/fabio/issues/371)
- Statsd output is not good [\#327](https://github.com/fabiolb/fabio/issues/327)
- Send metrics to cloudwatch [\#326](https://github.com/fabiolb/fabio/issues/326)
- Mixing of HTTPS proxying and SNI+TCP on a single port [\#213](https://github.com/fabiolb/fabio/issues/213)

**Merged pull requests:**

- gofmt [\#870](https://github.com/fabiolb/fabio/pull/870) ([nathanejohnson](https://github.com/nathanejohnson))
- updating x/sys [\#869](https://github.com/fabiolb/fabio/pull/869) ([nathanejohnson](https://github.com/nathanejohnson))
- update go and alpine versions [\#868](https://github.com/fabiolb/fabio/pull/868) ([Netlims](https://github.com/Netlims))
- \#865 Move the route table sort into NewTable so that it only happens once. [\#867](https://github.com/fabiolb/fabio/pull/867) ([ddreier](https://github.com/ddreier))
- removing exclusion of arm64 mac build.  Fixes \#804 [\#866](https://github.com/fabiolb/fabio/pull/866) ([nathanejohnson](https://github.com/nathanejohnson))
- Fix example commands in registry.consul.kvpath [\#864](https://github.com/fabiolb/fabio/pull/864) ([blake](https://github.com/blake))
- Add IdleConnTimeout configurable for http transport [\#863](https://github.com/fabiolb/fabio/pull/863) ([aal89](https://github.com/aal89))
- admin/ui updates: [\#857](https://github.com/fabiolb/fabio/pull/857) ([dcarbone](https://github.com/dcarbone))
- Update 2 broken links in documentation [\#822](https://github.com/fabiolb/fabio/pull/822) ([mig4ng](https://github.com/mig4ng))
- Fix small typo in README.md [\#821](https://github.com/fabiolb/fabio/pull/821) ([mig4ng](https://github.com/mig4ng))
- Add support for github actions [\#819](https://github.com/fabiolb/fabio/pull/819) ([nathanejohnson](https://github.com/nathanejohnson))
- Remove golang toolchain name from release binary names [\#818](https://github.com/fabiolb/fabio/pull/818) ([nathanejohnson](https://github.com/nathanejohnson))
- we don't use Fabio [\#813](https://github.com/fabiolb/fabio/pull/813) ([hsmade](https://github.com/hsmade))
- Updating tcp dynamic proxy to match on routes that are port only [\#806](https://github.com/fabiolb/fabio/pull/806) ([nathanejohnson](https://github.com/nathanejohnson))
- Refactor metrics [\#476](https://github.com/fabiolb/fabio/pull/476) ([magiconair](https://github.com/magiconair))

## [v1.5.15](https://github.com/fabiolb/fabio/tree/v1.5.15) (2020-12-01)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.14...v1.5.15)

**Closed issues:**

- TCP Dynamic Proxy is not releasing ports from deregistered services [\#796](https://github.com/fabiolb/fabio/issues/796)
- How to configure log file output path [\#781](https://github.com/fabiolb/fabio/issues/781)

**Merged pull requests:**

- Updating the default GOGC to 100.  800 proves to be a bit insane. [\#803](https://github.com/fabiolb/fabio/pull/803) ([nathanejohnson](https://github.com/nathanejohnson))
- Stop dynamic TCP listener when upstream is no longer available [\#798](https://github.com/fabiolb/fabio/pull/798) ([fwkz](https://github.com/fwkz))
- Updating dependencies [\#794](https://github.com/fabiolb/fabio/pull/794) ([nathanejohnson](https://github.com/nathanejohnson))
- Update CHANGELOG.md [\#790](https://github.com/fabiolb/fabio/pull/790) ([stevenscg](https://github.com/stevenscg))

## [v1.5.14](https://github.com/fabiolb/fabio/tree/v1.5.14) (2020-09-09)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.13...v1.5.14)

**Fixed bugs:**

- %20 in route is causing route mismatch,  regression in 1.5.2 , works with 1.3.7 [\#347](https://github.com/fabiolb/fabio/issues/347)

**Closed issues:**

- matchingHostNoGlob sometimes returns incorrect matched host [\#786](https://github.com/fabiolb/fabio/issues/786)
- Add support for HTTPS+TCP+SNI on the same listener [\#783](https://github.com/fabiolb/fabio/issues/783)
- SIGTERM + Gracefully closing connections [\#782](https://github.com/fabiolb/fabio/issues/782)
- passing multiple routes via command line [\#776](https://github.com/fabiolb/fabio/issues/776)
- Master branch build failing with SECURITY ERROR [\#769](https://github.com/fabiolb/fabio/issues/769)
- How to disable client authentication for https? [\#765](https://github.com/fabiolb/fabio/issues/765)
- Must Access Control require RemoteAddr matching? [\#754](https://github.com/fabiolb/fabio/issues/754)
- Fabio Proxy \(localhost:9999\) Showing Blank White Screen [\#752](https://github.com/fabiolb/fabio/issues/752)
- Fabio 1.5.13 - no more "\[INFO\] Config updates" message in the logs [\#751](https://github.com/fabiolb/fabio/issues/751)
- Authentication issue. [\#743](https://github.com/fabiolb/fabio/issues/743)
- Connecting to HTTPS Upstream service. [\#738](https://github.com/fabiolb/fabio/issues/738)
- log.routes.format is broken with 1.5.13 [\#737](https://github.com/fabiolb/fabio/issues/737)
- Looking for a new maintainer [\#735](https://github.com/fabiolb/fabio/issues/735)
- GRPC Proxy + HTTP Proxy, both useable at the same time? [\#734](https://github.com/fabiolb/fabio/issues/734)
- Trace spans all have the same operation name [\#732](https://github.com/fabiolb/fabio/issues/732)
-  consul: Error fetching config from /fabio/config. Get  [\#729](https://github.com/fabiolb/fabio/issues/729)
- Very frequent 502 errors  [\#721](https://github.com/fabiolb/fabio/issues/721)
- Fabio decodes URL path parameters [\#486](https://github.com/fabiolb/fabio/issues/486)
- http proxy error context canceled [\#264](https://github.com/fabiolb/fabio/issues/264)

**Merged pull requests:**

- Fixing issue \#786 - matchingHostNoGlob sometimes returns incorrect host [\#787](https://github.com/fabiolb/fabio/pull/787) ([nathanejohnson](https://github.com/nathanejohnson))
- updating documentation for pending 1.5.14 release [\#785](https://github.com/fabiolb/fabio/pull/785) ([nathanejohnson](https://github.com/nathanejohnson))
- https+tcp+sni listener support [\#784](https://github.com/fabiolb/fabio/pull/784) ([nathanejohnson](https://github.com/nathanejohnson))
- chore: fix typo in comments [\#775](https://github.com/fabiolb/fabio/pull/775) ([josgraha](https://github.com/josgraha))
- \(docs\): fixed small error [\#774](https://github.com/fabiolb/fabio/pull/774) ([0xflotus](https://github.com/0xflotus))
- Preserve table state by storing buffer table in fixed strings. [\#749](https://github.com/fabiolb/fabio/pull/749) ([aaronhurt](https://github.com/aaronhurt))
- only deploy once per build [\#747](https://github.com/fabiolb/fabio/pull/747) ([aaronhurt](https://github.com/aaronhurt))
- switch to github pages for doc hosting [\#746](https://github.com/fabiolb/fabio/pull/746) ([aaronhurt](https://github.com/aaronhurt))
- minor transition updates and small fixes [\#745](https://github.com/fabiolb/fabio/pull/745) ([aaronhurt](https://github.com/aaronhurt))
- switch back to travis CI [\#744](https://github.com/fabiolb/fabio/pull/744) ([nathanejohnson](https://github.com/nathanejohnson))
- follow hugo best practices [\#742](https://github.com/fabiolb/fabio/pull/742) ([aaronhurt](https://github.com/aaronhurt))
- Documentation updates for project transition. [\#740](https://github.com/fabiolb/fabio/pull/740) ([aaronhurt](https://github.com/aaronhurt))
- Fix infinite buffering of SSE responses when gzip is enabled [\#739](https://github.com/fabiolb/fabio/pull/739) ([ctlajoie](https://github.com/ctlajoie))
- Add missing \<svc\> entry to example route [\#733](https://github.com/fabiolb/fabio/pull/733) ([BenjaminHerbert](https://github.com/BenjaminHerbert))
- minor fixups [\#731](https://github.com/fabiolb/fabio/pull/731) ([aaronhurt](https://github.com/aaronhurt))
- fix tests [\#730](https://github.com/fabiolb/fabio/pull/730) ([aaronhurt](https://github.com/aaronhurt))
- Add HTTP method and path to trace span operation name [\#715](https://github.com/fabiolb/fabio/pull/715) ([hobochili](https://github.com/hobochili))
- Deprecate deregisterCriticalServiceAfter option [\#674](https://github.com/fabiolb/fabio/pull/674) ([pschultz](https://github.com/pschultz))
- Issue 647 NormalizeHost [\#648](https://github.com/fabiolb/fabio/pull/648) ([murphymj25](https://github.com/murphymj25))
- Handle context canceled errors + better http proxy error handling [\#644](https://github.com/fabiolb/fabio/pull/644) ([danlsgiga](https://github.com/danlsgiga))
- Added idleTimout to config and to serve.go HTTP server [\#635](https://github.com/fabiolb/fabio/pull/635) ([galen0624](https://github.com/galen0624))
- Issue 613 tcp dynamic [\#626](https://github.com/fabiolb/fabio/pull/626) ([murphymj25](https://github.com/murphymj25))
- Issue 554 Added compiled glob matching using LRU Cache [\#615](https://github.com/fabiolb/fabio/pull/615) ([galen0624](https://github.com/galen0624))
- Issue 558 - Add Polling Interval From Fabio to Consul to Fabio Config [\#572](https://github.com/fabiolb/fabio/pull/572) ([galen0624](https://github.com/galen0624))
- Feat: Pass encoded characters in path unchanged [\#489](https://github.com/fabiolb/fabio/pull/489) ([valentin-krasontovitsch](https://github.com/valentin-krasontovitsch))

## [v1.5.13](https://github.com/fabiolb/fabio/tree/v1.5.13) (2019-11-18)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.12...v1.5.13)

**Closed issues:**

- Fabio 1.5.12 - panic: runtime error: invalid memory address or nil pointer dereference [\#719](https://github.com/fabiolb/fabio/issues/719)
- Question: Load balancing WebSocket connections [\#718](https://github.com/fabiolb/fabio/issues/718)
- Question: resources \(css, js files\) by multiple sites [\#717](https://github.com/fabiolb/fabio/issues/717)
- Fabio UI not displaying when hit on a DNS name [\#712](https://github.com/fabiolb/fabio/issues/712)
- Unable to route to websites [\#676](https://github.com/fabiolb/fabio/issues/676)
- Websocket proxy timeouts [\#518](https://github.com/fabiolb/fabio/issues/518)

**Merged pull requests:**

- fix nil-pointer dereference in detailed config log [\#720](https://github.com/fabiolb/fabio/pull/720) ([pschultz](https://github.com/pschultz))
- Safely handle missing cert from Vault KV store [\#710](https://github.com/fabiolb/fabio/pull/710) ([dradtke](https://github.com/dradtke))

## [v1.5.12](https://github.com/fabiolb/fabio/tree/v1.5.12) (2019-10-11)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.11...v1.5.12)

**Implemented enhancements:**

- docker swarm some times register eth0 other eth1 [\#652](https://github.com/fabiolb/fabio/issues/652)
- config: let registry.consul.register.addr default to ui.addr [\#658](https://github.com/fabiolb/fabio/pull/658) ([pschultz](https://github.com/pschultz))
- fix exit status code [\#637](https://github.com/fabiolb/fabio/pull/637) ([ianic](https://github.com/ianic))

**Closed issues:**

- Example of Vault KV clientca option? [\#703](https://github.com/fabiolb/fabio/issues/703)
- tcp proxy not work [\#702](https://github.com/fabiolb/fabio/issues/702)
- urlprefix-:3306 proto=tcp   not work [\#701](https://github.com/fabiolb/fabio/issues/701)
- https proxy not work [\#700](https://github.com/fabiolb/fabio/issues/700)
- the http port is 9999 ,the https port is what? [\#699](https://github.com/fabiolb/fabio/issues/699)
- TCP proxy log filled with i/o timeout [\#696](https://github.com/fabiolb/fabio/issues/696)
- urlprefix-zzz.xxx.com/api  not work [\#693](https://github.com/fabiolb/fabio/issues/693)
- Fabio/Consul route integration [\#689](https://github.com/fabiolb/fabio/issues/689)
- Unable to route. [\#680](https://github.com/fabiolb/fabio/issues/680)
- Fabio 100% CPU usage due to logging [\#673](https://github.com/fabiolb/fabio/issues/673)
- Authorization header leaking to the backend. [\#671](https://github.com/fabiolb/fabio/issues/671)
- X-Request-Start header [\#670](https://github.com/fabiolb/fabio/issues/670)
- fabio service entries may stay in Consul on dirty exit [\#663](https://github.com/fabiolb/fabio/issues/663)
- Can fabio route request by request body [\#661](https://github.com/fabiolb/fabio/issues/661)
- Wrong reported HealthCheck-URI using custom -proxy.addr & -ui.addr [\#657](https://github.com/fabiolb/fabio/issues/657)
- Clarify documentation HTTP Redirects [\#656](https://github.com/fabiolb/fabio/issues/656)
- tcp access control doesn't work [\#651](https://github.com/fabiolb/fabio/issues/651)
- Crash on start of watchBackend\(\) [\#650](https://github.com/fabiolb/fabio/issues/650)
- Remove third-party cookie and script requirements from frontend [\#642](https://github.com/fabiolb/fabio/issues/642)
- Build should use included vendor directory with modules [\#638](https://github.com/fabiolb/fabio/issues/638)
- Route table UI is broken [\#628](https://github.com/fabiolb/fabio/issues/628)
- Possible Memory Leak in WatchBackend [\#595](https://github.com/fabiolb/fabio/issues/595)
- Release date for 1.5.11 [\#592](https://github.com/fabiolb/fabio/issues/592)
- Fabio and Vault Token Issues [\#523](https://github.com/fabiolb/fabio/issues/523)
- UI broken where no internet access. [\#502](https://github.com/fabiolb/fabio/issues/502)
- make log compatible with the syslog protocol [\#397](https://github.com/fabiolb/fabio/issues/397)

**Merged pull requests:**

- Add Vault example to the traffic shaping section. [\#677](https://github.com/fabiolb/fabio/pull/677) ([jrasell](https://github.com/jrasell))
- Fix matching priority for host:port tuples [\#675](https://github.com/fabiolb/fabio/pull/675) ([pschultz](https://github.com/pschultz))
- Add config option to use 128 bit trace IDs [\#669](https://github.com/fabiolb/fabio/pull/669) ([gfloyd](https://github.com/gfloyd))
- register: clean-up fabio service entries in Consul on dirty exit [\#664](https://github.com/fabiolb/fabio/pull/664) ([pires](https://github.com/pires))
- Fix SSE by implementing Flusher in responseWriter wrapper [\#655](https://github.com/fabiolb/fabio/pull/655) ([gfloyd](https://github.com/gfloyd))
- Use go-sockaddr to parse address strings [\#653](https://github.com/fabiolb/fabio/pull/653) ([aaronhurt](https://github.com/aaronhurt))
- ensure absolute path after strip to maintain rfc complaince [\#645](https://github.com/fabiolb/fabio/pull/645) ([aaronhurt](https://github.com/aaronhurt))
- Bundle UI assets [\#643](https://github.com/fabiolb/fabio/pull/643) ([pschultz](https://github.com/pschultz))
- ui: Remove duplicate destination column [\#641](https://github.com/fabiolb/fabio/pull/641) ([pschultz](https://github.com/pschultz))
- use vendor directory when building - fixes \#638 [\#639](https://github.com/fabiolb/fabio/pull/639) ([aaronhurt](https://github.com/aaronhurt))
- Issue 595 watchbackend [\#629](https://github.com/fabiolb/fabio/pull/629) ([murphymj25](https://github.com/murphymj25))
- added support for profile/tracing [\#624](https://github.com/fabiolb/fabio/pull/624) ([galen0624](https://github.com/galen0624))

## [v1.5.11](https://github.com/fabiolb/fabio/tree/v1.5.11) (2019-04-09)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.11-wrong...v1.5.11)

**Implemented enhancements:**

- Proxy protocol support fo outgoing connections [\#191](https://github.com/fabiolb/fabio/issues/191)

**Closed issues:**

- Consul blocking queries should be rate limited to avoid spiking loads on server [\#627](https://github.com/fabiolb/fabio/issues/627)
- This seems to be a recursive func call.  Is this correct? [\#625](https://github.com/fabiolb/fabio/issues/625)
- Bug in consul 1.4.3 [\#616](https://github.com/fabiolb/fabio/issues/616)
- \[question\] Release date for 1.5.11 [\#601](https://github.com/fabiolb/fabio/issues/601)
- Sidebar of the website is a little off [\#599](https://github.com/fabiolb/fabio/issues/599)
- wrong use  function strings.HasPrefix\(\) in file passsing.go [\#545](https://github.com/fabiolb/fabio/issues/545)
- best way to bypass fabio consul integration? [\#437](https://github.com/fabiolb/fabio/issues/437)

**Merged pull requests:**

- Issue 611 Added Custom API Driven Back end [\#614](https://github.com/fabiolb/fabio/pull/614) ([galen0624](https://github.com/galen0624))
- Improved basic auth htpasswd file refresh \#604 [\#610](https://github.com/fabiolb/fabio/pull/610) ([mfuterko](https://github.com/mfuterko))
- Address \#545 - wrong use function strings.HasPrefix [\#607](https://github.com/fabiolb/fabio/pull/607) ([mfuterko](https://github.com/mfuterko))
- docs: fix layout without JS enabled [\#606](https://github.com/fabiolb/fabio/pull/606) ([pschultz](https://github.com/pschultz))
- Implement basic auth htpasswd file refresh [\#604](https://github.com/fabiolb/fabio/pull/604) ([mfuterko](https://github.com/mfuterko))
- added support for Consul TLS transport [\#602](https://github.com/fabiolb/fabio/pull/602) ([sev3ryn](https://github.com/sev3ryn))
- Proxy protocol on outbound tcp, tcp+sni and tcp with tls connection [\#598](https://github.com/fabiolb/fabio/pull/598) ([mfuterko](https://github.com/mfuterko))

## [v1.5.11-wrong](https://github.com/fabiolb/fabio/tree/v1.5.11-wrong) (2019-02-25)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.10...v1.5.11-wrong)

**Implemented enhancements:**

- Basic authentication on routes [\#166](https://github.com/fabiolb/fabio/issues/166)

**Fixed bugs:**

- TCP proxy broken since v1.5.8 [\#524](https://github.com/fabiolb/fabio/issues/524)

**Closed issues:**

- Fabio's routing table empty. Consul indicates registered services with urlprefix- tags [\#589](https://github.com/fabiolb/fabio/issues/589)
- HTTP 502 response half of the time [\#584](https://github.com/fabiolb/fabio/issues/584)
- tcp+sni route with allow=ip:something does not seem to work [\#576](https://github.com/fabiolb/fabio/issues/576)
- Passing args to fabio in nomad task. [\#567](https://github.com/fabiolb/fabio/issues/567)
- Change Log entry update  [\#562](https://github.com/fabiolb/fabio/issues/562)
- Release date for 1.5.10? [\#560](https://github.com/fabiolb/fabio/issues/560)
- Route updates are delayed with large number of services  [\#558](https://github.com/fabiolb/fabio/issues/558)
- could the source and destination be clickable in the ui? [\#508](https://github.com/fabiolb/fabio/issues/508)
- Support for opentracing [\#429](https://github.com/fabiolb/fabio/issues/429)
- Case-insensitive path matching [\#35](https://github.com/fabiolb/fabio/issues/35)

**Merged pull requests:**

- ui: Fix XSS vulnerability [\#588](https://github.com/fabiolb/fabio/pull/588) ([pschultz](https://github.com/pschultz))
- make Dest column into clickable links [\#587](https://github.com/fabiolb/fabio/pull/587) ([kneufeld](https://github.com/kneufeld))
- update documentation around the changes to PROXY protocol [\#583](https://github.com/fabiolb/fabio/pull/583) ([aaronhurt](https://github.com/aaronhurt))
- address concerns raised while troubleshooting \#524 [\#581](https://github.com/fabiolb/fabio/pull/581) ([aaronhurt](https://github.com/aaronhurt))
- fix ip access rules within tcp proxy - fixes \#576 [\#577](https://github.com/fabiolb/fabio/pull/577) ([aaronhurt](https://github.com/aaronhurt))
- Add GRPC proxy support [\#575](https://github.com/fabiolb/fabio/pull/575) ([andyroyle](https://github.com/andyroyle))
- metrics.circonus: Add support for circonus.submissionurl [\#574](https://github.com/fabiolb/fabio/pull/574) ([stack72](https://github.com/stack72))
- add http-basic auth reading from a file [\#573](https://github.com/fabiolb/fabio/pull/573) ([andyroyle](https://github.com/andyroyle))
- update consul to v1.4.0 - fixes \#569 [\#571](https://github.com/fabiolb/fabio/pull/571) ([aaronhurt](https://github.com/aaronhurt))
- add faq to address \#490 [\#568](https://github.com/fabiolb/fabio/pull/568) ([aaronhurt](https://github.com/aaronhurt))
- Update go.mod for \#472 [\#565](https://github.com/fabiolb/fabio/pull/565) ([magiconair](https://github.com/magiconair))
- Refactor consul service monitor [\#564](https://github.com/fabiolb/fabio/pull/564) ([magiconair](https://github.com/magiconair))
- issue 562 update change log glob.matching.disabled [\#563](https://github.com/fabiolb/fabio/pull/563) ([galen0624](https://github.com/galen0624))
- Added new case insensitive matcher [\#553](https://github.com/fabiolb/fabio/pull/553) ([herbrandson](https://github.com/herbrandson))
- \[Docs\] Delete duplicate 'Path Stripping' page [\#537](https://github.com/fabiolb/fabio/pull/537) ([rkettelerij](https://github.com/rkettelerij))
- \#429 issue - OpenTrace zipKin Support  [\#472](https://github.com/fabiolb/fabio/pull/472) ([galen0624](https://github.com/galen0624))

## [v1.5.10](https://github.com/fabiolb/fabio/tree/v1.5.10) (2018-10-25)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.9...v1.5.10)

**Fixed bugs:**

- Wrong route for multiple matching host glob patterns [\#506](https://github.com/fabiolb/fabio/issues/506)

**Closed issues:**

- Fabio forcing response header keys upper case [\#552](https://github.com/fabiolb/fabio/issues/552)
- Multiple fabio instances load balancing different set of services. [\#551](https://github.com/fabiolb/fabio/issues/551)
- Without Consul how can i use Fabio? [\#549](https://github.com/fabiolb/fabio/issues/549)
- Performance issue - Glob matching with large number of services in consul [\#548](https://github.com/fabiolb/fabio/issues/548)
- Ignore host case when adding and matching routes [\#542](https://github.com/fabiolb/fabio/issues/542)
- allow redirect host to be empty [\#533](https://github.com/fabiolb/fabio/issues/533)
- Expose Fabio metrics via Prometheus [\#532](https://github.com/fabiolb/fabio/issues/532)
- Memory leak in go-metrics library [\#530](https://github.com/fabiolb/fabio/issues/530)
- ability to remove headers from the request [\#528](https://github.com/fabiolb/fabio/issues/528)
- urlprefix- does not work properly [\#527](https://github.com/fabiolb/fabio/issues/527)
- Problem geting fabio routing to its own ui [\#525](https://github.com/fabiolb/fabio/issues/525)
- Redirection to default back-end if route not exists [\#521](https://github.com/fabiolb/fabio/issues/521)
- Forwarding Uri tag in original request to endpoint [\#519](https://github.com/fabiolb/fabio/issues/519)
- Fabio - Manual overrides [\#515](https://github.com/fabiolb/fabio/issues/515)
- If consul is behind an ELB with a set timeout, and the connection is timed out by the ELB, subsequent requests from fabio fail [\#513](https://github.com/fabiolb/fabio/issues/513)
- Fabio instantly delete route, whereas health check is passing [\#512](https://github.com/fabiolb/fabio/issues/512)
- Would it be possible to configure Fabio to watch services with warning state? [\#509](https://github.com/fabiolb/fabio/issues/509)
- Headers passed through fabio are modified [\#505](https://github.com/fabiolb/fabio/issues/505)
- Fabio -\> HTTPS -\> Service ? [\#503](https://github.com/fabiolb/fabio/issues/503)
- Tls + sni support for non http traffic?  [\#499](https://github.com/fabiolb/fabio/issues/499)
- Static routes in fabio.properties [\#498](https://github.com/fabiolb/fabio/issues/498)
- Tests fail with consul \> 1.0.6 and vault \> 0.9.6 [\#494](https://github.com/fabiolb/fabio/issues/494)
- Question: wildcard hostname support [\#491](https://github.com/fabiolb/fabio/issues/491)
- Fabio confi help with multiple proto [\#490](https://github.com/fabiolb/fabio/issues/490)
- Add support for Vault 0.10 KV v2 [\#483](https://github.com/fabiolb/fabio/issues/483)
- Support "standard" Consul envvars [\#277](https://github.com/fabiolb/fabio/issues/277)
- Support Consul TLS [\#276](https://github.com/fabiolb/fabio/issues/276)

**Merged pull requests:**

- Issue \#548 added enable/disable glob matching [\#550](https://github.com/fabiolb/fabio/pull/550) ([galen0624](https://github.com/galen0624))
- Correct the access control feature documentation page [\#546](https://github.com/fabiolb/fabio/pull/546) ([msvbhat](https://github.com/msvbhat))
- Add $host pseudo variable [\#544](https://github.com/fabiolb/fabio/pull/544) ([holtwilkins](https://github.com/holtwilkins))
- compare host using lowercase [\#543](https://github.com/fabiolb/fabio/pull/543) ([shantanugadgil](https://github.com/shantanugadgil))
- Issue \#530 - Vendored in updated go-metrics package [\#535](https://github.com/fabiolb/fabio/pull/535) ([galen0624](https://github.com/galen0624))
- Add setting to flush fabio buffer regardless headers [\#531](https://github.com/fabiolb/fabio/pull/531) ([samm-git](https://github.com/samm-git))
- Update README.md [\#510](https://github.com/fabiolb/fabio/pull/510) ([kuskmen](https://github.com/kuskmen))
- Issue \#506: reverse domain names before sorting [\#507](https://github.com/fabiolb/fabio/pull/507) ([magiconair](https://github.com/magiconair))
- Fix changelog link in docs footer [\#500](https://github.com/fabiolb/fabio/pull/500) ([xmikus01](https://github.com/xmikus01))
- Make tests compatible with Vault 0.10 [\#497](https://github.com/fabiolb/fabio/pull/497) ([pschultz](https://github.com/pschultz))
- Delete an unused global variable logOutput [\#495](https://github.com/fabiolb/fabio/pull/495) ([gua-pian](https://github.com/gua-pian))
- Add fastcgi handler [\#435](https://github.com/fabiolb/fabio/pull/435) ([Gufran](https://github.com/Gufran))

## [v1.5.9](https://github.com/fabiolb/fabio/tree/v1.5.9) (2018-05-16)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.8...v1.5.9)

**Closed issues:**

- UI is broken from  versions =\> 1.7 [\#487](https://github.com/fabiolb/fabio/issues/487)
- Building master fails [\#482](https://github.com/fabiolb/fabio/issues/482)
- '-registry.consul.register.enabled' does not seem to be respected [\#467](https://github.com/fabiolb/fabio/issues/467)
- Access logging fails in combination with proxy gzipping [\#460](https://github.com/fabiolb/fabio/issues/460)
- glob matching improvements [\#452](https://github.com/fabiolb/fabio/issues/452)
- Add route based on x-forwarded-port header [\#450](https://github.com/fabiolb/fabio/issues/450)
- Redirect http to https on the same destination [\#448](https://github.com/fabiolb/fabio/issues/448)
- WebSocket Upgrade not sending Response [\#447](https://github.com/fabiolb/fabio/issues/447)
- Fabio does not remove service when one of the registered health-checks fail [\#427](https://github.com/fabiolb/fabio/issues/427)
- Fabio routing to wrong back end [\#421](https://github.com/fabiolb/fabio/issues/421)
- \[feature\]: proxy route option [\#356](https://github.com/fabiolb/fabio/issues/356)

**Merged pull requests:**

- Resetting read deadline [\#492](https://github.com/fabiolb/fabio/pull/492) ([craigday](https://github.com/craigday))
- Issue \#466: make redirect code more robust [\#477](https://github.com/fabiolb/fabio/pull/477) ([magiconair](https://github.com/magiconair))
- fix contributors link [\#475](https://github.com/fabiolb/fabio/pull/475) ([aaronhurt](https://github.com/aaronhurt))
- ws close on failed handshake \(\#421\) [\#474](https://github.com/fabiolb/fabio/pull/474) ([magiconair](https://github.com/magiconair))
- Issue \#460: Fix access logging when gzip is enabled [\#470](https://github.com/fabiolb/fabio/pull/470) ([magiconair](https://github.com/magiconair))
- Fix the regex of the example proxy.gzip.contenttype [\#468](https://github.com/fabiolb/fabio/pull/468) ([tino](https://github.com/tino))
- Check upstream X-Forwarded-Proto prior to redirect [\#466](https://github.com/fabiolb/fabio/pull/466) ([aaronhurt](https://github.com/aaronhurt))
- Fix certificate stores doc path [\#458](https://github.com/fabiolb/fabio/pull/458) ([eldondev](https://github.com/eldondev))
- Add new & improved glob matcher [\#457](https://github.com/fabiolb/fabio/pull/457) ([sharbov](https://github.com/sharbov))
- handle indeterminate length proxy chains - fixes \#449 [\#453](https://github.com/fabiolb/fabio/pull/453) ([aaronhurt](https://github.com/aaronhurt))
- Update link for Websockets [\#446](https://github.com/fabiolb/fabio/pull/446) ([a2ar](https://github.com/a2ar))
- "strict" health-checking \(\#427\) [\#428](https://github.com/fabiolb/fabio/pull/428) ([systemfreund](https://github.com/systemfreund))

## [v1.5.8](https://github.com/fabiolb/fabio/tree/v1.5.8) (2018-02-18)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.7...v1.5.8)

**Closed issues:**

- TCP Proxying SSH connections [\#445](https://github.com/fabiolb/fabio/issues/445)
- route add ... opts "proto=tcp+sni" ?? [\#444](https://github.com/fabiolb/fabio/issues/444)
- Wildcard registeration issues [\#440](https://github.com/fabiolb/fabio/issues/440)
- Feature Request: IP Whitelisting [\#439](https://github.com/fabiolb/fabio/issues/439)
- NoRouteHTMLPath not rendering HTML page [\#438](https://github.com/fabiolb/fabio/issues/438)

**Merged pull requests:**

- ignore fabio.exe [\#443](https://github.com/fabiolb/fabio/pull/443) ([aaronhurt](https://github.com/aaronhurt))
- Issue \#438: Do not add separators for NoRouteHTML page [\#441](https://github.com/fabiolb/fabio/pull/441) ([magiconair](https://github.com/magiconair))
- Add option to allow Fabio to register frontend services in Consul on behalf of user services [\#426](https://github.com/fabiolb/fabio/pull/426) ([rileyje](https://github.com/rileyje))
- TCP+SNI support arbitrary large Client Hello [\#423](https://github.com/fabiolb/fabio/pull/423) ([DanSipola](https://github.com/DanSipola))

## [v1.5.7](https://github.com/fabiolb/fabio/tree/v1.5.7) (2018-02-06)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.6...v1.5.7)

**Closed issues:**

- VaultPKI tests fail with go1.10rc1 [\#434](https://github.com/fabiolb/fabio/issues/434)
- Ensure that proxy.noroutestatus has three digits [\#433](https://github.com/fabiolb/fabio/issues/433)
- Vault PKI documentation and Fabio version [\#430](https://github.com/fabiolb/fabio/issues/430)
- configure equivalent  of nginx client\_max\_body\_size [\#422](https://github.com/fabiolb/fabio/issues/422)
- \[question\] Newbie question: where to place urlpref-host/path? [\#419](https://github.com/fabiolb/fabio/issues/419)
- Static / Manual routes management via API [\#396](https://github.com/fabiolb/fabio/issues/396)
- Warn if fabio is run as root [\#369](https://github.com/fabiolb/fabio/issues/369)

**Merged pull requests:**

- Activating Open Collective [\#432](https://github.com/fabiolb/fabio/pull/432) ([monkeywithacupcake](https://github.com/monkeywithacupcake))
- fix small typo [\#431](https://github.com/fabiolb/fabio/pull/431) ([aaronhurt](https://github.com/aaronhurt))
- Add support for HSTS response headers and provide method for adding additional response headers [\#425](https://github.com/fabiolb/fabio/pull/425) ([aaronhurt](https://github.com/aaronhurt))
- Fix maxconn documentation [\#420](https://github.com/fabiolb/fabio/pull/420) ([slobo](https://github.com/slobo))
- treat registry.consul.kvpath as prefix [\#417](https://github.com/fabiolb/fabio/pull/417) ([magiconair](https://github.com/magiconair))
- Issue \#369: Do not allow to run fabio as root [\#377](https://github.com/fabiolb/fabio/pull/377) ([magiconair](https://github.com/magiconair))

## [v1.5.6](https://github.com/fabiolb/fabio/tree/v1.5.6) (2018-01-05)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.5...v1.5.6)

**Closed issues:**

- Excessive consul logging [\#408](https://github.com/fabiolb/fabio/issues/408)
- Build new website [\#405](https://github.com/fabiolb/fabio/issues/405)
- \[bug?\] Fabio uses "global" Consul ServiceID's [\#383](https://github.com/fabiolb/fabio/issues/383)

**Merged pull requests:**

- Issue \#408: log consul state changes as DEBUG [\#418](https://github.com/fabiolb/fabio/pull/418) ([magiconair](https://github.com/magiconair))
- Actually respect -version option [\#415](https://github.com/fabiolb/fabio/pull/415) ([pschultz](https://github.com/pschultz))
- Identify services using both the ID and the Node [\#414](https://github.com/fabiolb/fabio/pull/414) ([alvaroaleman](https://github.com/alvaroaleman))

## [v1.5.5](https://github.com/fabiolb/fabio/tree/v1.5.5) (2017-12-20)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.4...v1.5.5)

**Implemented enhancements:**

- Support custom 404/503 error pages [\#56](https://github.com/fabiolb/fabio/issues/56)

**Closed issues:**

- Fabio for task/container/service load balancing on amazon ecs with consul and registrator.  [\#402](https://github.com/fabiolb/fabio/issues/402)

**Merged pull requests:**

- Implement custom noroute html response [\#398](https://github.com/fabiolb/fabio/pull/398) ([tino](https://github.com/tino))

## [v1.5.4](https://github.com/fabiolb/fabio/tree/v1.5.4) (2017-12-10)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.3...v1.5.4)

**Implemented enhancements:**

- Differentiate "URL Unavailable/503" and "URL Not Found/404" [\#214](https://github.com/fabiolb/fabio/issues/214)

**Fixed bugs:**

- opts with host= with multiple routes does not work as expected [\#385](https://github.com/fabiolb/fabio/issues/385)

**Closed issues:**

- Fabio is not handling SIGHUP \(HUP\) signal properly - it dies [\#400](https://github.com/fabiolb/fabio/issues/400)
- Typo in manual overrides stops Fabio from updating routes [\#399](https://github.com/fabiolb/fabio/issues/399)
- route precendence  [\#389](https://github.com/fabiolb/fabio/issues/389)
- how to connect consul cluster [\#386](https://github.com/fabiolb/fabio/issues/386)
- Allow comments in manual overrides [\#379](https://github.com/fabiolb/fabio/issues/379)
- Domain or protocol redirection [\#87](https://github.com/fabiolb/fabio/issues/87)
- Should rewrite the Host Header  [\#75](https://github.com/fabiolb/fabio/issues/75)

**Merged pull requests:**

- Issue \#400: ignore SIGHUP [\#403](https://github.com/fabiolb/fabio/pull/403) ([magiconair](https://github.com/magiconair))
- Issue \#389: match exact host before glob matches [\#390](https://github.com/fabiolb/fabio/pull/390) ([magiconair](https://github.com/magiconair))
- Issue \#385: attach options to target instead of route [\#388](https://github.com/fabiolb/fabio/pull/388) ([magiconair](https://github.com/magiconair))
- Fix various minor things [\#382](https://github.com/fabiolb/fabio/pull/382) ([antham](https://github.com/antham))
- Remove unused variable [\#381](https://github.com/fabiolb/fabio/pull/381) ([antham](https://github.com/antham))
- Now setting the X-Forwarded-Host header if not present. Add matching … [\#380](https://github.com/fabiolb/fabio/pull/380) ([LeReverandNox](https://github.com/LeReverandNox))

## [v1.5.3](https://github.com/fabiolb/fabio/tree/v1.5.3) (2017-11-03)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.2...v1.5.3)

**Implemented enhancements:**

- Drop privileges after start [\#195](https://github.com/fabiolb/fabio/issues/195)
- support for adding CORS headers? [\#110](https://github.com/fabiolb/fabio/issues/110)

**Closed issues:**

- host=www.mydomain.com not working [\#375](https://github.com/fabiolb/fabio/issues/375)
- Wildcards in routing path [\#374](https://github.com/fabiolb/fabio/issues/374)
- Questions/issues in using overrides [\#372](https://github.com/fabiolb/fabio/issues/372)
- nodes and services in maintenance can cause excessive logging [\#367](https://github.com/fabiolb/fabio/issues/367)
- Support fabio.properties in Consul KV store [\#365](https://github.com/fabiolb/fabio/issues/365)
- Fabio fails to strip the prefix if the url prefix does not start with the strip option value [\#363](https://github.com/fabiolb/fabio/issues/363)
- More than one fabio instance decreases system performance. [\#361](https://github.com/fabiolb/fabio/issues/361)
- Documentation of the available metrics? [\#360](https://github.com/fabiolb/fabio/issues/360)
- select color scheme from config to distinguish environments [\#359](https://github.com/fabiolb/fabio/issues/359)
- \[Feature request\]: TCP Proxy support different incoming and outbound ports [\#353](https://github.com/fabiolb/fabio/issues/353)
- hgfiii [\#351](https://github.com/fabiolb/fabio/issues/351)
- statsd - unable to parse line - gf metric [\#350](https://github.com/fabiolb/fabio/issues/350)
- Possibility for Docker Image to pass Consul IP and Port as Variable? [\#346](https://github.com/fabiolb/fabio/issues/346)
- Ways to have log verbosity [\#345](https://github.com/fabiolb/fabio/issues/345)
- Cant disable consul register with -registry.consul.register.enabled=false [\#342](https://github.com/fabiolb/fabio/issues/342)
- Glob Matcher is not working for me [\#341](https://github.com/fabiolb/fabio/issues/341)
- Strip option has no effect for websockets [\#330](https://github.com/fabiolb/fabio/issues/330)
- access logging is not right [\#322](https://github.com/fabiolb/fabio/issues/322)
- FATAL error when metrics cannot be delivered [\#320](https://github.com/fabiolb/fabio/issues/320)
- http: proxy error: context canceled [\#318](https://github.com/fabiolb/fabio/issues/318)
- /api/routes intermittently returns null. [\#316](https://github.com/fabiolb/fabio/issues/316)
- what is the tcp writeTimeout? [\#307](https://github.com/fabiolb/fabio/issues/307)

**Merged pull requests:**

- Issue \#375: set host header when host option is set [\#376](https://github.com/fabiolb/fabio/pull/376) ([magiconair](https://github.com/magiconair))

## [v1.5.2](https://github.com/fabiolb/fabio/tree/v1.5.2) (2017-07-24)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.1...v1.5.2)

**Implemented enhancements:**

- Auto-generated Vault certs [\#135](https://github.com/fabiolb/fabio/issues/135)

**Closed issues:**

- not able to acces the service via fabio. [\#319](https://github.com/fabiolb/fabio/issues/319)

**Merged pull requests:**

- Fix memory leak in tcp proxy [\#321](https://github.com/fabiolb/fabio/pull/321) ([Crypto89](https://github.com/Crypto89))

## [v1.5.1](https://github.com/fabiolb/fabio/tree/v1.5.1) (2017-07-06)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.5.0...v1.5.1)

**Implemented enhancements:**

- Feature: Allow weight tag in Consul [\#42](https://github.com/fabiolb/fabio/issues/42)

**Fixed bugs:**

- 1.5.0 config compatibility problem  [\#305](https://github.com/fabiolb/fabio/issues/305)

**Closed issues:**

- Multiple urlprefix [\#317](https://github.com/fabiolb/fabio/issues/317)
- Add metrics for TCP and TCP+SNI proxy [\#306](https://github.com/fabiolb/fabio/issues/306)
- How to configure TCP correctly \(proxy.addr, ...\) [\#283](https://github.com/fabiolb/fabio/issues/283)
- Add parameter to vault token renewal [\#274](https://github.com/fabiolb/fabio/issues/274)

**Merged pull requests:**

- Issue \#274: Avoid premature vault token renewals [\#314](https://github.com/fabiolb/fabio/pull/314) ([pschultz](https://github.com/pschultz))
- Make tests work with vault 0.7.x [\#313](https://github.com/fabiolb/fabio/pull/313) ([pschultz](https://github.com/pschultz))
- Fix syntax highlighting in README [\#311](https://github.com/fabiolb/fabio/pull/311) ([agis](https://github.com/agis))

## [v1.5.0](https://github.com/fabiolb/fabio/tree/v1.5.0) (2017-06-07)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.4.4...v1.5.0)

**Implemented enhancements:**

- X-Forwarded-Prefix header support [\#304](https://github.com/fabiolb/fabio/issues/304)
- read only web ui [\#302](https://github.com/fabiolb/fabio/issues/302)
- Sync X-Forwarded-Proto and Forwarded header when possible [\#296](https://github.com/fabiolb/fabio/issues/296)
- Using upstream hostname for request [\#294](https://github.com/fabiolb/fabio/issues/294)
- Add profiling support [\#290](https://github.com/fabiolb/fabio/issues/290)
- TLS and Connection information through headers [\#280](https://github.com/fabiolb/fabio/issues/280)
- Support TLS/Ciphersuite configuration options [\#249](https://github.com/fabiolb/fabio/issues/249)

**Fixed bugs:**

- Support gzip compression for websockets [\#300](https://github.com/fabiolb/fabio/issues/300)

**Closed issues:**

- Example of proxy.gzip.contenttype configuration [\#299](https://github.com/fabiolb/fabio/issues/299)
- Compatibility with 1.8 [\#297](https://github.com/fabiolb/fabio/issues/297)
- cert file names and path= not working as documented [\#293](https://github.com/fabiolb/fabio/issues/293)
- Multiple SSL certs for same listener [\#291](https://github.com/fabiolb/fabio/issues/291)
- HTTPProxy cannot be aware of timeout of waiting response [\#288](https://github.com/fabiolb/fabio/issues/288)
- websockets failing with 500 response - running rancher behind fabio [\#133](https://github.com/fabiolb/fabio/issues/133)

**Merged pull requests:**

- Using upstream hostname for request \(\#294\) [\#301](https://github.com/fabiolb/fabio/pull/301) ([mitchelldavis](https://github.com/mitchelldavis))

## [v1.4.4](https://github.com/fabiolb/fabio/tree/v1.4.4) (2017-05-08)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.4.3...v1.4.4)

**Implemented enhancements:**

- Add service name to access log fields [\#278](https://github.com/fabiolb/fabio/issues/278)

**Fixed bugs:**

- Fabio does not advertise http/1.1 on TLS connections [\#289](https://github.com/fabiolb/fabio/issues/289)
- fabio does not start with multiple listen sockets [\#279](https://github.com/fabiolb/fabio/issues/279)
- Websocket not working with HTTPS Upstream [\#271](https://github.com/fabiolb/fabio/issues/271)

**Closed issues:**

- Reload configuration without restarting fabio by SIGHUP or by flag. [\#286](https://github.com/fabiolb/fabio/issues/286)
- chunked Transfer-Encoding [\#284](https://github.com/fabiolb/fabio/issues/284)
- How to know what opts are supported in a route / consul tag? [\#270](https://github.com/fabiolb/fabio/issues/270)
- Question: Support for Consul v0.7.3 Node tags [\#252](https://github.com/fabiolb/fabio/issues/252)

## [v1.4.3](https://github.com/fabiolb/fabio/tree/v1.4.3) (2017-04-24)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.4.2...v1.4.3)

**Fixed bugs:**

- Access log cannot be disabled [\#269](https://github.com/fabiolb/fabio/issues/269)

**Closed issues:**

- Can fabio proxy by hostname? [\#267](https://github.com/fabiolb/fabio/issues/267)
- Issues with Haproxy on passthrough mode [\#266](https://github.com/fabiolb/fabio/issues/266)
- How to configure HTTPS upstream manually with tlsskipverify [\#260](https://github.com/fabiolb/fabio/issues/260)
- HTTPS upstream added as HTTP [\#259](https://github.com/fabiolb/fabio/issues/259)

**Merged pull requests:**

- Add support for TLSSkipVerify for https consul fabio check [\#268](https://github.com/fabiolb/fabio/pull/268) ([Ginja](https://github.com/Ginja))

## [v1.4.2](https://github.com/fabiolb/fabio/tree/v1.4.2) (2017-04-10)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.4.1...v1.4.2)

**Implemented enhancements:**

- Add HTTPS upstream support [\#181](https://github.com/fabiolb/fabio/issues/181)

**Closed issues:**

- Find the route across the machine, but no response [\#256](https://github.com/fabiolb/fabio/issues/256)

**Merged pull requests:**

- Allow UI/API to be served over https [\#258](https://github.com/fabiolb/fabio/pull/258) ([tmessi](https://github.com/tmessi))
- Add https upstream support [\#257](https://github.com/fabiolb/fabio/pull/257) ([tmessi](https://github.com/tmessi))

## [v1.4.1](https://github.com/fabiolb/fabio/tree/v1.4.1) (2017-04-04)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.4...v1.4.1)

**Implemented enhancements:**

- Add generic TCP proxying support [\#179](https://github.com/fabiolb/fabio/issues/179)
- Add tests and timeouts to TCP+SNI proxy [\#178](https://github.com/fabiolb/fabio/issues/178)

**Closed issues:**

- Is there any option to enable HSTS [\#254](https://github.com/fabiolb/fabio/issues/254)

## [v1.4](https://github.com/fabiolb/fabio/tree/v1.4) (2017-03-25)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.4rc1...v1.4)

## [v1.4rc1](https://github.com/fabiolb/fabio/tree/v1.4rc1) (2017-03-23)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.4beta2...v1.4rc1)

## [v1.4beta2](https://github.com/fabiolb/fabio/tree/v1.4beta2) (2017-03-23)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.4beta1...v1.4beta2)

## [v1.4beta1](https://github.com/fabiolb/fabio/tree/v1.4beta1) (2017-03-23)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.3.8...v1.4beta1)

**Implemented enhancements:**

- Start listener after routing table is initialized [\#248](https://github.com/fabiolb/fabio/issues/248)
- Support glob host matching [\#163](https://github.com/fabiolb/fabio/issues/163)
- Refactor urlprefix tags [\#111](https://github.com/fabiolb/fabio/issues/111)
- TCP proxying support [\#1](https://github.com/fabiolb/fabio/issues/1)

**Closed issues:**

- feature idea: fabio can be configured to only serve consul services with certain tags [\#245](https://github.com/fabiolb/fabio/issues/245)
- How does services get in to router table of fabio [\#237](https://github.com/fabiolb/fabio/issues/237)

## [v1.3.8](https://github.com/fabiolb/fabio/tree/v1.3.8) (2017-02-14)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.3.7...v1.3.8)

**Implemented enhancements:**

- Retry registry during startup [\#240](https://github.com/fabiolb/fabio/issues/240)
- Make route update logging format configurable [\#238](https://github.com/fabiolb/fabio/issues/238)
- Support absolute URLs [\#219](https://github.com/fabiolb/fabio/issues/219)

**Fixed bugs:**

- requests and notfound metric missing [\#218](https://github.com/fabiolb/fabio/issues/218)
- fabio 1.3.6 UI displays host and path as 'undefined' in the routes page [\#217](https://github.com/fabiolb/fabio/issues/217)

**Closed issues:**

- https support [\#241](https://github.com/fabiolb/fabio/issues/241)
- Fabio - setup details [\#235](https://github.com/fabiolb/fabio/issues/235)
- Not able to connect to fabio UI ... I wonder if I miss any specifics ?. [\#234](https://github.com/fabiolb/fabio/issues/234)
- Error in Fabio setup on container where consul-agent \(client\) is installed [\#233](https://github.com/fabiolb/fabio/issues/233)
- Fabio Connecting error to local consul-agent \(client\) [\#232](https://github.com/fabiolb/fabio/issues/232)
- Load balancing between multiple service cluster nodes [\#231](https://github.com/fabiolb/fabio/issues/231)
- Specify Consul service name in Fabio config [\#230](https://github.com/fabiolb/fabio/issues/230)
- caching [\#228](https://github.com/fabiolb/fabio/issues/228)
- Links in docs to the Traffic Shaping page are dead [\#222](https://github.com/fabiolb/fabio/issues/222)
- Overrides API and GUI save KV Store as wrong name [\#220](https://github.com/fabiolb/fabio/issues/220)

## [v1.3.7](https://github.com/fabiolb/fabio/tree/v1.3.7) (2017-01-19)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.3.6...v1.3.7)

**Implemented enhancements:**

- Support deleting routes by tag [\#201](https://github.com/fabiolb/fabio/issues/201)

**Fixed bugs:**

- Fabio does not serve http2 with go \>= 1.7 [\#215](https://github.com/fabiolb/fabio/issues/215)
- Bad statsd mean metric format [\#207](https://github.com/fabiolb/fabio/issues/207)

**Closed issues:**

- Fabio is not able to pick service from consul and not able to update routing table. [\#210](https://github.com/fabiolb/fabio/issues/210)

## [v1.3.6](https://github.com/fabiolb/fabio/tree/v1.3.6) (2017-01-17)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.3.5...v1.3.6)

**Implemented enhancements:**

- Refactor config loader tests [\#199](https://github.com/fabiolb/fabio/issues/199)
- Routing by path [\#164](https://github.com/fabiolb/fabio/issues/164)
- Strip prefix in the forwarded request [\#44](https://github.com/fabiolb/fabio/issues/44)

**Fixed bugs:**

- runtime error: integer divide by zero [\#186](https://github.com/fabiolb/fabio/issues/186)

**Closed issues:**

- fabio proxy for consul not work, log show no route [\#212](https://github.com/fabiolb/fabio/issues/212)
- Consul registration won't disable [\#209](https://github.com/fabiolb/fabio/issues/209)
- Fabio hangs for 30+ seconds for 204 response [\#206](https://github.com/fabiolb/fabio/issues/206)
- Fabio running using Nomad system scheduler breaks Docker.  [\#192](https://github.com/fabiolb/fabio/issues/192)

## [v1.3.5](https://github.com/fabiolb/fabio/tree/v1.3.5) (2016-11-30)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.3.4...v1.3.5)

**Implemented enhancements:**

- fabio --version switch should work just like -v [\#197](https://github.com/fabiolb/fabio/issues/197)
- Remove proxy.header.tls header from inbound request [\#194](https://github.com/fabiolb/fabio/issues/194)
- Support transparent response body compression [\#119](https://github.com/fabiolb/fabio/issues/119)

**Fixed bugs:**

- missing 'cs' in map [\#189](https://github.com/fabiolb/fabio/issues/189)
- WebSockets not working with IE10 - header casing. [\#183](https://github.com/fabiolb/fabio/issues/183)
- Vault CA Certificate [\#182](https://github.com/fabiolb/fabio/issues/182)

**Closed issues:**

- Logs request [\#188](https://github.com/fabiolb/fabio/issues/188)
- Is this the expecting behavior of Fabio with paths? [\#187](https://github.com/fabiolb/fabio/issues/187)
- TCP+SNI support on the same port as HTTPS  [\#169](https://github.com/fabiolb/fabio/issues/169)

## [v1.3.4](https://github.com/fabiolb/fabio/tree/v1.3.4) (2016-10-28)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.3.3...v1.3.4)

## [v1.3.3](https://github.com/fabiolb/fabio/tree/v1.3.3) (2016-10-12)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.3.2...v1.3.3)

**Implemented enhancements:**

- Provide linux/arm and linux/arm64 binaries [\#161](https://github.com/fabiolb/fabio/issues/161)
- Metrics Prefix with templates [\#160](https://github.com/fabiolb/fabio/pull/160) ([md2k](https://github.com/md2k))

**Fixed bugs:**

- TCP+SNI proxy does not work with PROXY protocol [\#177](https://github.com/fabiolb/fabio/issues/177)
- Consul cert store URL with token not parsed correctly [\#172](https://github.com/fabiolb/fabio/issues/172)
- Panic on invalid response [\#159](https://github.com/fabiolb/fabio/issues/159)

**Closed issues:**

- can not see new application added to the same fabio instance [\#176](https://github.com/fabiolb/fabio/issues/176)
- Ridiculous lack for docker documentation [\#175](https://github.com/fabiolb/fabio/issues/175)
- OT: logo for the eBay organization [\#158](https://github.com/fabiolb/fabio/issues/158)

**Merged pull requests:**

- Use Go's net.JoinHostPort which will auto-detect ipv6 [\#167](https://github.com/fabiolb/fabio/pull/167) ([jovandeginste](https://github.com/jovandeginste))

## [v1.3.2](https://github.com/fabiolb/fabio/tree/v1.3.2) (2016-09-11)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.3.1...v1.3.2)

**Fixed bugs:**

- ParseListen may set the wrong protocol [\#157](https://github.com/fabiolb/fabio/issues/157)

## [v1.3.1](https://github.com/fabiolb/fabio/tree/v1.3.1) (2016-09-09)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.3...v1.3.1)

## [v1.3](https://github.com/fabiolb/fabio/tree/v1.3) (2016-09-09)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.2.1...v1.3)

**Implemented enhancements:**

- Add support for Circonus metrics [\#151](https://github.com/fabiolb/fabio/issues/151)
- Support multiple metrics libraries [\#147](https://github.com/fabiolb/fabio/issues/147)
- Is there a way to prevent SSL requests falling back to an unrelated cert? [\#138](https://github.com/fabiolb/fabio/issues/138)
- Vault token should not require 'root' or 'sudo' privileges [\#134](https://github.com/fabiolb/fabio/issues/134)
- Extended metrics [\#125](https://github.com/fabiolb/fabio/issues/125)

**Fixed bugs:**

- fabio fails to start with "\[FATAL\] 1.2. missing 'cs' in cs" [\#146](https://github.com/fabiolb/fabio/issues/146)

**Closed issues:**

- fabio g-rpc [\#156](https://github.com/fabiolb/fabio/issues/156)
- Routing based on Accept Header [\#155](https://github.com/fabiolb/fabio/issues/155)
- not all command-line options seem to do anything [\#152](https://github.com/fabiolb/fabio/issues/152)

## [v1.2.1](https://github.com/fabiolb/fabio/tree/v1.2.1) (2016-08-25)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.2...v1.2.1)

**Implemented enhancements:**

- Server-sent events support [\#129](https://github.com/fabiolb/fabio/issues/129)
- access logging [\#80](https://github.com/fabiolb/fabio/issues/80)
- Support configuration via command line arguments [\#79](https://github.com/fabiolb/fabio/issues/79)
- Support statsd [\#73](https://github.com/fabiolb/fabio/issues/73)
- SSL Certs from Vault [\#70](https://github.com/fabiolb/fabio/issues/70)
- Refactor listener config [\#28](https://github.com/fabiolb/fabio/issues/28)
- Add/remove certificates using API [\#27](https://github.com/fabiolb/fabio/issues/27)

**Fixed bugs:**

- Always deregister from Consul [\#136](https://github.com/fabiolb/fabio/issues/136)

**Closed issues:**

- HA access to the management interface on instances [\#145](https://github.com/fabiolb/fabio/issues/145)
- Fabio is not adding route, but health check is passing [\#142](https://github.com/fabiolb/fabio/issues/142)
- Wrong Destination IP [\#140](https://github.com/fabiolb/fabio/issues/140)
- Having trouble recognizing routes from consul [\#137](https://github.com/fabiolb/fabio/issues/137)

**Merged pull requests:**

- Improve error message on missing trailing slash [\#143](https://github.com/fabiolb/fabio/pull/143) ([juliangamble](https://github.com/juliangamble))
- added statsd support [\#139](https://github.com/fabiolb/fabio/pull/139) ([jshaw86](https://github.com/jshaw86))

## [v1.2](https://github.com/fabiolb/fabio/tree/v1.2) (2016-07-16)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.2rc4...v1.2)

**Fixed bugs:**

- fabio 1.2rc3 panics with -v [\#128](https://github.com/fabiolb/fabio/issues/128)

## [v1.2rc4](https://github.com/fabiolb/fabio/tree/v1.2rc4) (2016-07-13)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.2rc3...v1.2rc4)

## [v1.2rc3](https://github.com/fabiolb/fabio/tree/v1.2rc3) (2016-07-12)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1.6...v1.2rc3)

## [v1.1.6](https://github.com/fabiolb/fabio/tree/v1.1.6) (2016-07-12)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.2rc2...v1.1.6)

**Implemented enhancements:**

- TLS handshake error: failed to verify client's certificate [\#108](https://github.com/fabiolb/fabio/issues/108)

**Fixed bugs:**

- X-Forwarded-Port should use local port [\#122](https://github.com/fabiolb/fabio/issues/122)

**Closed issues:**

- Path problem [\#124](https://github.com/fabiolb/fabio/issues/124)

## [v1.2rc2](https://github.com/fabiolb/fabio/tree/v1.2rc2) (2016-06-23)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1.5...v1.2rc2)

## [v1.1.5](https://github.com/fabiolb/fabio/tree/v1.1.5) (2016-06-23)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.2rc1...v1.1.5)

**Implemented enhancements:**

- Allow routes to a service in warning status [\#117](https://github.com/fabiolb/fabio/pull/117) ([erikvanoosten](https://github.com/erikvanoosten))

**Closed issues:**

- Fabio hangs for 30+ seconds for 204 response [\#120](https://github.com/fabiolb/fabio/issues/120)

## [v1.2rc1](https://github.com/fabiolb/fabio/tree/v1.2rc1) (2016-06-15)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1.4...v1.2rc1)

## [v1.1.4](https://github.com/fabiolb/fabio/tree/v1.1.4) (2016-06-15)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1.3...v1.1.4)

**Implemented enhancements:**

- Custom status code when no route found [\#107](https://github.com/fabiolb/fabio/issues/107)
- Keep fabio registered in consul [\#100](https://github.com/fabiolb/fabio/issues/100)
- Disable fabio health check in consul [\#99](https://github.com/fabiolb/fabio/issues/99)
- Support PROXY protocol [\#97](https://github.com/fabiolb/fabio/issues/97)

**Closed issues:**

- fabio should expose a /health endpoint  [\#112](https://github.com/fabiolb/fabio/issues/112)
- Go 1.5 issue [\#109](https://github.com/fabiolb/fabio/issues/109)

## [v1.1.3](https://github.com/fabiolb/fabio/tree/v1.1.3) (2016-05-19)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1.3rc2...v1.1.3)

**Implemented enhancements:**

- Keep sort order in UI stable [\#104](https://github.com/fabiolb/fabio/issues/104)
- Trim whitespace around tag [\#103](https://github.com/fabiolb/fabio/issues/103)
- SNI support? [\#85](https://github.com/fabiolb/fabio/issues/85)

## [v1.1.3rc2](https://github.com/fabiolb/fabio/tree/v1.1.3rc2) (2016-05-14)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1.3rc1...v1.1.3rc2)

**Implemented enhancements:**

- Add glob path matching \(an alternative to default prefix matching\) [\#93](https://github.com/fabiolb/fabio/pull/93) ([dkong](https://github.com/dkong))

## [v1.1.3rc1](https://github.com/fabiolb/fabio/tree/v1.1.3rc1) (2016-05-09)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1.2...v1.1.3rc1)

**Implemented enhancements:**

- Improve forward headers [\#98](https://github.com/fabiolb/fabio/issues/98)
- Allow tags for fabio service registration [\#96](https://github.com/fabiolb/fabio/issues/96)
- Expand experimental HTTP API [\#95](https://github.com/fabiolb/fabio/issues/95)
- Drop default port from request [\#90](https://github.com/fabiolb/fabio/issues/90)
- Use Address instead of ServiceAddress? [\#88](https://github.com/fabiolb/fabio/issues/88)
- Expand ${DC} to consul datacenter [\#55](https://github.com/fabiolb/fabio/issues/55)

**Closed issues:**

- proxy handler error channel bug? [\#92](https://github.com/fabiolb/fabio/issues/92)

## [v1.1.2](https://github.com/fabiolb/fabio/tree/v1.1.2) (2016-04-27)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1.1...v1.1.2)

**Fixed bugs:**

- Deleted routes hide visible routes [\#57](https://github.com/fabiolb/fabio/issues/57)

**Closed issues:**

- Recommended way to bind multiple fabio instances to public IP for HA [\#89](https://github.com/fabiolb/fabio/issues/89)
- Windows support [\#86](https://github.com/fabiolb/fabio/issues/86)
- How to load balance '/'? [\#83](https://github.com/fabiolb/fabio/issues/83)
- register websockets with consul tags [\#82](https://github.com/fabiolb/fabio/issues/82)
- fabio does not respect registry\_consul\_register\_ip from ENV [\#77](https://github.com/fabiolb/fabio/issues/77)
- Not deregistering when consul health status fails  [\#71](https://github.com/fabiolb/fabio/issues/71)
- question: configure through environment variables? [\#68](https://github.com/fabiolb/fabio/issues/68)
- support middleware\(OWIN\) to execute some code before recirection [\#64](https://github.com/fabiolb/fabio/issues/64)

**Merged pull requests:**

- \#77 fix documentaion [\#78](https://github.com/fabiolb/fabio/pull/78) ([sielaq](https://github.com/sielaq))
- Expose the docker ports in Dockerfile [\#76](https://github.com/fabiolb/fabio/pull/76) ([smancke](https://github.com/smancke))
- Overworked header handling. [\#74](https://github.com/fabiolb/fabio/pull/74) ([smancke](https://github.com/smancke))
- Broken link corrected. [\#65](https://github.com/fabiolb/fabio/pull/65) ([jest](https://github.com/jest))

## [v1.1.1](https://github.com/fabiolb/fabio/tree/v1.1.1) (2016-02-22)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1...v1.1.1)

**Merged pull requests:**

- Fix use of local ip in consul service registration [\#58](https://github.com/fabiolb/fabio/pull/58) ([jeanblanchard](https://github.com/jeanblanchard))

## [v1.1](https://github.com/fabiolb/fabio/tree/v1.1) (2016-02-18)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.1rc1...v1.1)

**Implemented enhancements:**

- Make read and write timeout configurable [\#53](https://github.com/fabiolb/fabio/issues/53)

## [v1.1rc1](https://github.com/fabiolb/fabio/tree/v1.1rc1) (2016-02-15)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.9...v1.1rc1)

## [v1.0.9](https://github.com/fabiolb/fabio/tree/v1.0.9) (2016-02-15)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.8...v1.0.9)

**Implemented enhancements:**

- Allow configuration of serviceip used during consul registration [\#48](https://github.com/fabiolb/fabio/issues/48)
- Allow configuration via env vars [\#43](https://github.com/fabiolb/fabio/issues/43)
- Cleanup metrics for deleted routes [\#41](https://github.com/fabiolb/fabio/issues/41)
- HTTP2 support with latest Go [\#32](https://github.com/fabiolb/fabio/issues/32)
- Support additional backends [\#12](https://github.com/fabiolb/fabio/issues/12)

**Fixed bugs:**

- Include services with check ids other than 'service:\*' [\#29](https://github.com/fabiolb/fabio/issues/29)

**Closed issues:**

- Move dependencies to vendor path [\#47](https://github.com/fabiolb/fabio/issues/47)
- Add support for Consul ACL token to demo server [\#37](https://github.com/fabiolb/fabio/issues/37)

## [v1.0.8](https://github.com/fabiolb/fabio/tree/v1.0.8) (2016-01-14)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.7...v1.0.8)

**Implemented enhancements:**

- Consul ACL Token [\#36](https://github.com/fabiolb/fabio/issues/36)

**Fixed bugs:**

- Detect when consul agent is down [\#26](https://github.com/fabiolb/fabio/issues/26)
- fabio route not removed after consul deregister [\#22](https://github.com/fabiolb/fabio/issues/22)

**Closed issues:**

- Session persistence [\#33](https://github.com/fabiolb/fabio/issues/33)
- Build fails on master/last release tag [\#31](https://github.com/fabiolb/fabio/issues/31)
- Documentation: make build before running ./fabio [\#24](https://github.com/fabiolb/fabio/issues/24)

**Merged pull requests:**

- \[registry\] fallback to given local IP address [\#30](https://github.com/fabiolb/fabio/pull/30) ([doublerebel](https://github.com/doublerebel))

## [v1.0.7](https://github.com/fabiolb/fabio/tree/v1.0.7) (2015-12-13)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.6...v1.0.7)

**Fixed bugs:**

- routes not removed when passing empty string [\#23](https://github.com/fabiolb/fabio/issues/23)

**Closed issues:**

- server demo: Consul health check fails [\#21](https://github.com/fabiolb/fabio/issues/21)
- Demo \(shebang, documentation\) [\#20](https://github.com/fabiolb/fabio/issues/20)
- \(Docker\) Error initializing backend. [\#19](https://github.com/fabiolb/fabio/issues/19)

## [v1.0.6](https://github.com/fabiolb/fabio/tree/v1.0.6) (2015-12-01)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.5...v1.0.6)

**Implemented enhancements:**

- Filter routing table not on tags [\#16](https://github.com/fabiolb/fabio/issues/16)
- Support websockets [\#9](https://github.com/fabiolb/fabio/issues/9)

**Fixed bugs:**

- Traffic shaping does not match on service name [\#15](https://github.com/fabiolb/fabio/issues/15)

**Closed issues:**

- Manage manual overrides via UI [\#18](https://github.com/fabiolb/fabio/issues/18)

**Merged pull requests:**

- README: fix typos [\#14](https://github.com/fabiolb/fabio/pull/14) ([ceh](https://github.com/ceh))

## [v1.0.5](https://github.com/fabiolb/fabio/tree/v1.0.5) (2015-11-11)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.4...v1.0.5)

**Implemented enhancements:**

- Support Forwarded and X-Forwarded-For headers [\#10](https://github.com/fabiolb/fabio/issues/10)

**Merged pull requests:**

- fix vet warning [\#13](https://github.com/fabiolb/fabio/pull/13) ([juliendsv](https://github.com/juliendsv))

## [v1.0.4](https://github.com/fabiolb/fabio/tree/v1.0.4) (2015-11-03)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.3...v1.0.4)

**Implemented enhancements:**

- Support SSL/TLS client cert authentication [\#8](https://github.com/fabiolb/fabio/issues/8)

**Closed issues:**

- List among Consul community tools [\#6](https://github.com/fabiolb/fabio/issues/6)

**Merged pull requests:**

- Fixes broken fragment identifier link [\#11](https://github.com/fabiolb/fabio/pull/11) ([budnik](https://github.com/budnik))

## [v1.0.3](https://github.com/fabiolb/fabio/tree/v1.0.3) (2015-10-26)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.2...v1.0.3)

**Merged pull requests:**

- Correcting a typo [\#5](https://github.com/fabiolb/fabio/pull/5) ([mdevreugd](https://github.com/mdevreugd))

## [v1.0.2](https://github.com/fabiolb/fabio/tree/v1.0.2) (2015-10-23)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.1...v1.0.2)

**Merged pull requests:**

- Honor consul.url and consul.addr from config file [\#3](https://github.com/fabiolb/fabio/pull/3) ([jeinwag](https://github.com/jeinwag))

## [v1.0.1](https://github.com/fabiolb/fabio/tree/v1.0.1) (2015-10-21)

[Full Changelog](https://github.com/fabiolb/fabio/compare/v1.0.0...v1.0.1)



\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of Conduct

Please respect others and treat them the way you want to
be treated yourself.

If you feel someone overstepped please reach out to one of the
[project owners](https://github.com/orgs/fabiolb/teams/owners) and someone will follow up.

Thank you

The Fabio Team


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing Guidelines

More information on how to contribute to fabio can be found on 
the [wiki](https://github.com/fabiolb/fabio/wiki/Contributing)


## Financial contributions

We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/fabio).
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.


## Credits


### Contributors

Thank you to all the people who have already contributed to fabio!
<a href="graphs/contributors"><img src="https://opencollective.com/fabio/contributors.svg?width=890" /></a>


### Backers

Thank you to all our backers! [[Become a backer](https://opencollective.com/fabio#backer)]

<a href="https://opencollective.com/fabio#backers" target="_blank"><img src="https://opencollective.com/fabio/backers.svg?width=890"></a>


### Sponsors

Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/fabio#sponsor))

<a href="https://opencollective.com/fabio/sponsor/0/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/fabio/sponsor/1/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/fabio/sponsor/2/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/fabio/sponsor/3/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/fabio/sponsor/4/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/fabio/sponsor/5/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/fabio/sponsor/6/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/fabio/sponsor/7/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/fabio/sponsor/8/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/fabio/sponsor/9/website" target="_blank"><img src="https://opencollective.com/fabio/sponsor/9/avatar.svg"></a>

================================================
FILE: Dockerfile
================================================
FROM golang AS build

ARG TARGETARCH
ARG consul_version=1.22.0
ADD https://releases.hashicorp.com/consul/${consul_version}/consul_${consul_version}_linux_${TARGETARCH}.zip /usr/local/bin
RUN cd /usr/local/bin && unzip consul_${consul_version}_linux_${TARGETARCH}.zip consul

ARG vault_version=1.21.0
ADD https://releases.hashicorp.com/vault/${vault_version}/vault_${vault_version}_linux_${TARGETARCH}.zip /usr/local/bin
RUN cd /usr/local/bin && unzip vault_${vault_version}_linux_${TARGETARCH}.zip vault

RUN apt-get update && apt-get install -y git ca-certificates libcap2-bin
WORKDIR /src
COPY . .
RUN go mod tidy
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -trimpath -ldflags "-s -w" -o /src/fabio
RUN setcap cap_net_bind_service=+ep /src/fabio
RUN echo "nobody:x:65534:65534:nobody:/:/sbin/nologin" > /passwd
RUN echo "nogroup:x:65533:" > /group

FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=build /src/fabio /usr/bin/
COPY --from=build /passwd /etc/
COPY --from=build /group /etc/
ADD --chown=nobody:nogroup fabio.properties /etc/fabio/fabio.properties
USER nobody:nogroup
EXPOSE 9998 9999
ENTRYPOINT ["/usr/bin/fabio"]
CMD ["-cfg", "/etc/fabio/fabio.properties"]


================================================
FILE: Dockerfile-goreleaser
================================================
FROM debian:stable-slim AS build
RUN apt-get update && apt-get install -y git ca-certificates libcap2-bin
ADD fabio /usr/bin/
RUN setcap CAP_NET_BIND_SERVICE=+eip /usr/bin/fabio
RUN echo "nobody:x:65534:65534:nobody:/:/sbin/nologin" > /passwd
RUN echo "nogroup:x:65533:" > /group

FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=build /usr/bin/fabio /usr/bin/
COPY --from=build /passwd /etc/
COPY --from=build /group /etc/
ADD --chown=nobody:nogroup fabio.properties /etc/fabio/fabio.properties
USER nobody:nogroup
EXPOSE 9998 9999
ENTRYPOINT ["/usr/bin/fabio"]
CMD ["-cfg", "/etc/fabio/fabio.properties"]


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2020 Education Networks of America.  All rights reserved.
Copyright (c) 2017-2020 Frank Schroeder. All rights reserved. (after 15 Apr 2017/commit 38f73da6413b68fed1631101ac1d0b79a2fac870)
Copyright (c) 2015-2017 eBay Software Foundation. All rights reserved. (before 15 Apr 2017/commit 38f73da6413b68fed1631101ac1d0b79a2fac870)

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.


================================================
FILE: Makefile
================================================
# CUR_TAG is the last git tag plus the delta from the current commit to the tag
# e.g. v1.5.5-<nr of commits since>-g<current git sha>
CUR_TAG ?= $(shell git describe --tags --first-parent)

# LAST_TAG is the last git tag
# e.g. v1.5.5
LAST_TAG ?= $(shell git describe --tags --first-parent --abbrev=0)

# VERSION is the last git tag without the 'v'
# e.g. 1.5.5
VERSION ?= $(shell git describe --tags --first-parent --abbrev=0 | cut -c 2-)


# GOFLAGS is the flags for the go compiler.
GOFLAGS ?= -ldflags "-X main.version=$(CUR_TAG)"

# GOVERSION is the current go version, e.g. go1.9.2
GOVERSION ?= $(shell go version | awk '{print $$3;}')

# GORELEASER is the path to the goreleaser binary.
GORELEASER ?= $(shell which goreleaser)

# pin versions for CI builds
CI_CONSUL_VERSION ?= 1.22.0
CI_VAULT_VERSION ?= 1.21.0
CI_HUGO_VERSION ?= 0.142.0
CI_GOBGP_VERSION ?= 3.37.0

BETA_OSES = linux darwin

# all is the default target
all: test

# help prints a help screen
help:
	@echo "generate  - go generate (use it when updating admin ui assets)"
	@echo "build     - go build"
	@echo "install   - go install"
	@echo "test      - go test"
	@echo "gofmt     - go fmt"
	@echo "linux     - go build linux/amd64"
	@echo "release   - tag, build and publish release with goreleaser"
	@echo "pkg       - build, test and create pkg/fabio.tar.gz"
	@echo "clean-adm - remove admin ui assets"
	@echo "clean     - remove temp files"
	@echo "clean-all - execute all clean commands"

# generate executes all go:generate statements
.PHONY: generate
generate: clean-adm
	go generate $(GOFLAGS) ./...

# build compiles fabio and the test dependencies
.PHONY: build
build: gofmt
	go build $(GOFLAGS)

# test builds and runs the tests
.PHONY: test
test: build
	go test $(GOFLAGS) -v -test.timeout 15s ./...

# mod performs go module maintenance
.PHONY: mod
mod:
	go mod tidy

# gofmt runs gofmt on the code
.PHONY: gofmt
gofmt:
	gofmt -s -w `find . -type f -name '*.go'`


beta: $(BETA_OSES)
beta:
	sha256sum fabio_$(CUR_TAG)_*_amd64 > fabio_$(CUR_TAG).sha256 fabio.properties
	gpg -b fabio_$(CUR_TAG).sha256
	tar czvf fabio_$(CUR_TAG).tar.gz fabio.properties fabio_$(CUR_TAG)_*_amd64 fabio_$(CUR_TAG).sha256 fabio_$(CUR_TAG).sha256.sig
$(BETA_OSES):
	CGO_ENABLED=0 GOOS=$@ GOARCH=amd64 go build -trimpath -tags netgo $(GOFLAGS) -o fabio_$(CUR_TAG)_$@_amd64

# install runs go install
.PHONY: install
install:
	CGO_ENABLED=0 go install -trimpath $(GOFLAGS)

# pkg builds a fabio.tar.gz package with only fabio in it
.PHONY: pkg
pkg: build test
	rm -rf pkg
	mkdir pkg
	tar czf pkg/fabio.tar.gz fabio

# release tags, builds and publishes a build with goreleaser
#
# Run this in sub-shells instead of dependencies so that
# later targets can pick up the new tag value.
.PHONY: release
release:
	$(MAKE) tag
	$(MAKE) preflight docker-test gorelease homebrew

# preflight runs some checks before a release
.PHONY: preflight
preflight:
	[ "$(CUR_TAG)" == "$(LAST_TAG)" ] || ( echo "master not tagged. Last tag is $(LAST_TAG)" ; exit 1 )
	grep -q "$(LAST_TAG)" CHANGELOG.md main.go || ( echo "CHANGELOG.md or main.go not updated. $(LAST_TAG) not found"; exit 1 )

# tag tags the build
.PHONY: tag
tag:
	build/tag.sh

# gorelease runs goreleaser to build and publish the artifacts
.PHONY: gorelease .RELEASE.CHANGELOG.md
gorelease: changelog
	[ -x "$(GORELEASER)" ] || ( echo "goreleaser not installed"; exit 1)
	GOVERSION=$(GOVERSION) goreleaser --rm-dist --release-notes=.RELEASE.CHANGELOG.md

.PHONY: goreleasedryrun
goreleasedryrun: changelog
	[ -x "$(GORELEASER)" ] || ( echo "goreleaser not installed"; exit 1)
	GOVERSION=$(GOVERSION) goreleaser --rm-dist --skip-publish --skip-validate --release-notes=.RELEASE.CHANGELOG.md

.PHONY: changelog
changelog:
	RELEASE=$(CUR_TAG) build/releasenotes.pl <CHANGELOG.md > .RELEASE.CHANGELOG.md
# homebrew updates the brew recipe since goreleaser can only
# handle taps right now.
.PHONY: homebrew
homebrew:
	build/homebrew.sh $(LAST_TAG)

# docker-test runs make test in a Docker container with
# pinned versions of the external dependencies
#
# We download the binaries outside the Docker build to
# cache the binaries and prevent repeated downloads since
# ADD <url> downloads the file every time.
.PHONY: docker-test
docker-test:
	docker build \
		--build-arg consul_version=$(CI_CONSUL_VERSION) \
		--build-arg vault_version=$(CI_VAULT_VERSION) \
		-t test-fabio \
		-f Dockerfile \
		.

# travis runs tests on Travis CI
.PHONY: travis
travis:
	wget -q -O ~/consul.zip https://releases.hashicorp.com/consul/$(CI_CONSUL_VERSION)/consul_$(CI_CONSUL_VERSION)_linux_amd64.zip
	wget -q -O ~/vault.zip https://releases.hashicorp.com/vault/$(CI_VAULT_VERSION)/vault_$(CI_VAULT_VERSION)_linux_amd64.zip
	unzip -o -d ~/bin ~/consul.zip
	unzip -o -d ~/bin ~/vault.zip
	vault --version
	consul --version
	make test

# travis-pages runs the GitHub pages (https://fabiolb.net/) deploy on Travis CI
.PHONY: travis-pages
travis-pages:
	wget -q -O ~/hugo.tgz https://github.com/gohugoio/hugo/releases/download/v$(CI_HUGO_VERSION)/hugo_$(CI_HUGO_VERSION)_Linux-64bit.tar.gz
	tar -C ~/bin -zxf ~/hugo.tgz hugo
	hugo version
	(cd docs && hugo --verbose)

# github runs tests on github actions
.PHONY: github
github:
	wget -q -O ~/consul.zip https://releases.hashicorp.com/consul/$(CI_CONSUL_VERSION)/consul_$(CI_CONSUL_VERSION)_linux_amd64.zip
	wget -q -O ~/vault.zip https://releases.hashicorp.com/vault/$(CI_VAULT_VERSION)/vault_$(CI_VAULT_VERSION)_linux_amd64.zip
	wget -q -O ~/gobgp.tar.gz https://github.com/osrg/gobgp/releases/download/v$(CI_GOBGP_VERSION)/gobgp_$(CI_GOBGP_VERSION)_linux_amd64.tar.gz
	unzip -o -d ~/bin ~/consul.zip
	unzip -o -d ~/bin ~/vault.zip
	tar xzf ~/gobgp.tar.gz -C ~/bin
	vault --version
	consul --version
	make test

# github-pages runs the GitHub pages (https://fabiolb.net/) deploy on github actions
.PHONY: github-pages
github-pages:
	wget -q -O ~/hugo.tgz https://github.com/gohugoio/hugo/releases/download/v$(CI_HUGO_VERSION)/hugo_$(CI_HUGO_VERSION)_Linux-64bit.tar.gz
	mkdir -p ~/bin
	tar -C ~/bin -zxf ~/hugo.tgz hugo
	hugo version
	(cd docs && hugo)

# clean-adm cleans up all downloaded assets in admin/ui
.PHONY: clean-adm
clean-adm:
	rm -rf admin/ui/assets/cdnjs.cloudflare.com/ajax/libs/materialize
	rm -f admin/ui/assets/code.jquery.com/*.js
	rm -f admin/ui/assets/fonts/material*
	rm -f admin/ui/assets/fonts/Material*

# clean removes intermediate files
.PHONY: clean
clean:
	go clean
	rm -rf pkg dist fabio
	find . -name '*.test' -delete

.PHONY: all help build test mod gofmt linux install pkg release preflight tag gorelease homebrew docker-test travis travis-pages clean-all beta BETA_OSES

# clean-all executes all "clean*" commands
.PHONY: clean-all
clean-all: clean clean-adm


================================================
FILE: NOTICES.txt
================================================
fabio

https://github.com/fabiolb/fabio
License: MIT (https://github.com/fabiolb/fabio/LICENSE)
Copyright (c) 2017 Frank Schroeder. All rights reserved. (after 15 Apr 2017/commit 38f73da6413b68fed1631101ac1d0b79a2fac870)
Copyright (c) 2015 eBay Software Foundation. All rights reserved. (before 15 Apr 2017/commit 38f73da6413b68fed1631101ac1d0b79a2fac870)


------------------------------------------------
Attribution for Project Dependencies
------------------------------------------------

github.com/armon/go-proxyproto
https://github.com/armon/go-proxyproto
License: MIT (https://github.com/armon/go-proxyproto/LICENSE)
Copyright (c) 2014 Armon Dadgar


github.com/circonus-labs/circonus-gometrics
https://github.com/circonus-labs/circonus-gometrics
License: BSD 3-clause (https://github.com/circonus-labs/circonus-gometrics/LICENSE)
Copyright (c) 2016, Circonus, Inc. All rights reserved.


github.com/circonus-labs/circonusllhist
https://github.com/circonus-labs/circonusllhist
License: BSD 3-clause (https://github.com/circonus-labs/circonusllhist/LICENSE)
Copyright (c) 2016 Circonus, Inc. All rights reserved.


github.com/cyberdelia/go-metrics-graphite
https://github.com/cyberdelia/go-metrics-graphite
License: BSD 2-clause (https://github.com/cyberdelia/go-metrics-graphite/LICENSE)
Copyright 2015 Timothée Peignier. All rights reserved.


github.com/fatih/structs
https://github.com/fatih/structs
License: MIT (https://github.com/fatih/structs/LICENSE)
Copyright (c) 2014 Fatih Arslan


github.com/hashicorp/consul
https://github.com/hashicorp/consul
License: MPL-2 (https://github.com/hashicorp/consul/LICENSE)
Copyright 2017 HashiCorp, Inc.


github.com/hashicorp/errwrap
https://github.com/hashicorp/errwrap
License: MPL-2 (https://github.com/hashicorp/errwrap/LICENSE)
Copyright 2017 HashiCorp, Inc.


github.com/hashicorp/go-cleanhttp
https://github.com/hashicorp/go-cleanhttp
License: MPL-2 (https://github.com/hashicorp/go-cleanhttp/LICENSE)
Copyright 2017 HashiCorp, Inc.


github.com/hashicorp/go-multierror
https://github.com/hashicorp/go-multierror
License: MPL-2 (https://github.com/hashicorp/go-multierror/LICENSE)
Copyright 2017 HashiCorp, Inc.


github.com/hashicorp/go-retryablehttp
https://github.com/hashicorp/go-retryablehttp
License: MPL-2 (https://github.com/hashicorp/go-retryablehttp/LICENSE)
Copyright 2017 HashiCorp, Inc.


github.com/hashicorp/go-rootcerts
https://github.com/hashicorp/go-rootcerts
License: MPL-2 (https://github.com/hashicorp/go-rootcerts/LICENSE)
Copyright 2017 HashiCorp, Inc.


github.com/hashicorp/hcl
https://github.com/hashicorp/hcl
License: MPL-2 (https://github.com/hashicorp/hcl/LICENSE)
Copyright 2017 HashiCorp, Inc.


github.com/hashicorp/serf
https://github.com/hashicorp/serf
License: MPL-2 (https://github.com/hashicorp/serf/LICENSE)
Copyright 2017 HashiCorp, Inc.


github.com/hashicorp/vault
https://github.com/hashicorp/vault
License: MPL-2 (https://github.com/hashicorp/vault/LICENSE)
Copyright 2017 HashiCorp, Inc.


github.com/pubnub/go-metrics-statsd
https://github.com/pubnub/go-metrics-statsd
License: MIT (https://github.com/pubnub/go-metrics-statsd/LICENSE)
Copyright (c) 2016 PubNub


github.com/magiconair/properties
https://github.com/magiconair/properties
License: BSD 2-clause (https://github.com/magiconair/properties/LICENSE)
Copyright (c) 2013-2017 - Frank Schroeder


github.com/mitchellh/go-homedir
https://github.com/mitchellh/go-homedir
License: MIT (https://github.com/mitchellh/go-homedir/LICENSE)
Copyright (c) 2013 Mitchell Hashimoto


github.com/mitchellh/mapstructure
https://github.com/mitchellh/mapstructure
License: MIT (https://github.com/mitchellh/mapstructure/LICENSE)
Copyright (c) 2013 Mitchell Hashimoto


github.com/rcrowley/go-metrics
https://github.com/rcrowley/go-metrics
License: BSD 2-clause (https://github.com/rcrowley/go-metrics/LICENSE)
Copyright 2012 Richard Crowley. All rights reserved.


github.com/ryanuber/go-glob
https://github.com/ryanuber/go-glob
License: MIT (https://github.com/ryanuber/go-glob/LICENSE)
Copyright (c) 2014 Ryan Uber


github.com/sergi/go-diff
https://github.com/sergi/go-diff
License: MIT (https://github.com/sergi/go-diff/LICENSE)
Copyright (c) 2012-2016 The go-diff Authors. All rights reserved.


github.com/rakyll/statik
https://github.com/rakyll/statik
License: Apache-2.0 (https://github.com/rakyll/statik/LICENSE)
Copyright (c) 2014 Google Inc. All Rights Reserved.


MaterializeCSS
https://materializecss.com/
License: MIT (https://github.com/dogfalo/materialize/LICENSE)
Copyright (c) 2018 Materialize


jQuery
https://jquery.com/
License: MIT (https://github.com/jquery/jquery/LICENSE)
Copyright (c) JS Foundation and other contributors, https://js.foundation/


Material Design icons
http://google.github.io/material-design-icons/
License: Apache-2.0 (https://github.com/google/material-design-icons/LICENSE)
Copyright 2015 Google, Inc. All Rights Reserved.


golang.org/x/net
https://golang.org/x/net
License: BSD 3-clause (https://golang.org/x/net/LICENSE)
Copyright (c) 2009 The Go Authors. All rights reserved.


golang.org/go
https://github.com/golang/go
License: BSD 3-clause (https://github.com/golang/go/LICENSE)
Copyright (c) 2009 The Go Authors. All rights reserved.


github.com/rogpeppe/fastuuid
https://github.com/rogpeppe/fastuuid.git
License: BSD 3-clause (https://github.com/google/uuid/LICENSE)
Copyright © 2014, Roger Peppe All rights reserved.

golang.org/x/sync/singleflight
https://golang.org/x/sync/singleflight
License: BSD 3-clause (https://golang.org/x/sync/LICENSE)
Copyright (c) 2009 The Go Authors. All rights reserved.


================================================
FILE: README.md
================================================
<p align="center">
  <p align="center" style="width: 50%; height: 64px;">
    <img src="https://cdn.rawgit.com/fabiolb/fabio/015e999/fabio.svg" height="64"/>
  </p>
  <p align="center" style="margin-top: 16px">
    <a href="http://ebay.github.io/"><img src="https://cdn.rawgit.com/fabiolb/fabio/7a02e1f/ebay.png" height="32" style="padding-right: 4px"/></a>
    <a href="http://www.ebayclassifiedsgroup.com"><img src="https://cdn.rawgit.com/fabiolb/fabio/7a02e1f/ecg.png" height="32"/></a>
    <a href="http://www.mytaxi.de"><img src="https://cdn.rawgit.com/fabiolb/fabio/7a02e1f/mytaxi.png" height="32"/></a>
    <a href="http://www.classmarkets.com"><img src="https://cdn.rawgit.com/fabiolb/fabio/7a02e1f/classmarkets.png" height="32"/></a>
  </p>
  <p align="center" style="margin-top: 16px">
    <a href="https://github.com/fabiolb/fabio/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/fabiolb/fabio.svg?style=flat-square"></a>
    <a href="https://raw.githubusercontent.com/fabiolb/fabio/master/LICENSE"><img alt="License MIT" src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square"></a>
    <a href="https://github.com/fabiolb/fabio/actions/workflows/build.yml"><img alt="Github Actions Build Status" src="https://github.com/fabiolb/fabio/actions/workflows/build.yml/badge.svg"></a>
    <a href="https://github.com/fabiolb/fabio/releases"><img alt="Downloads" src="https://img.shields.io/github/downloads/fabiolb/fabio/total.svg?style=flat-square"></a>
    <a href="https://hub.docker.com/r/fabiolb/fabio/"><img alt="Docker Pulls fabiolb" src="https://img.shields.io/docker/pulls/fabiolb/fabio.svg?style=flat-square&label=docker+pulls+fabiolb"></a>
  </p>
</p>

---

#### Notes

1) From release 1.6.1 onward, the minimum golang version supported is 1.16. 
2) From release 1.6.0 onward, metrics backend statsd is no longer supported.  statsd_raw
works similarly, though it actually resets counters appropriately.  If you are using datadog,
you should consider using the new dogstatsd backend, which has support for tags now.  Graphite
histogram functionality has changed slightly since switching to gokit framework, so something to be aware of.
 Prometheus functionality is now supported natively.

3) From release 1.5.15 onward, fabio changes the default GOGC from 800 back to
the golang default of 100.  Apparently this made some sense back in the golang 1.5 days, but with
changes introduced with golang 1.12 and others, this is probably no longer a very good default.
This is still configurable, as always, but the new default should make the most sense for most users.

4) From release 1.5.14, release hashes are signed with a new PGP key.
See details [here](https://fabiolb.net/faq/verifying-releases/).

5) From release 1.5.14 onward, fabio binary releases are compiled with golang 1.15+.  
This means that the fabio will no longer validate upstream https certificates that do 
not have SAN extensions matching the server name.  This may be a concern if fabio is 
communicating with https backends with misconfigured certificates.  If this is a problem,
you can specify `tlsskipverify=true` on the route.



---

fabio is a fast, modern, zero-conf load balancing HTTP(S) and TCP router
for deploying applications managed by [consul](https://consul.io/).

Register your services in consul, provide a health check and fabio will start
routing traffic to them. No configuration required. Deployment, upgrading and
refactoring has never been easier.

fabio is developed and maintained by The Fabio Authors.

It powers some of the largest websites in
Australia ([gumtree.com.au](http://www.gumtree.com.au)).
It delivers 23.000 req/sec every day since Sep 2015 without problems.

It integrates with
[Consul](https://consul.io/),
[Vault](https://vaultproject.io/),
[Amazon ELB](https://aws.amazon.com/elasticloadbalancing),
[Amazon API Gateway](https://aws.amazon.com/api-gateway/)
and more.

It supports ([Full feature list](https://fabiolb.net/feature/))

* [TLS termination with dynamic certificate stores](https://fabiolb.net/feature/certificate-stores/)
* [Raw TCP proxy](https://fabiolb.net/feature/tcp-proxy/)
* [TCP+SNI proxy for full end-to-end TLS](https://fabiolb.net/feature/tcp-sni-proxy/) without decryption
* [HTTPS+TCP+SNI proxy for TCP+SNI with HTTPS fallback](https://fabiolb.net/feature/https-tcp-sni-proxy/)
* [TCP dynamic proxy](https://fabiolb.net/feature/tcp-dynamic-proxy/)
* [HTTPS upstream support](https://fabiolb.net/feature/https-upstream/)
* [Websockets](https://fabiolb.net/feature/websockets/) and
* [SSE](https://fabiolb.net/feature/sse/)
* [Dynamic reloading without restart](https://fabiolb.net/feature/dynamic-reloading/)
* [Traffic shaping](https://fabiolb.net/feature/traffic-shaping/) for "blue/green" deployments,
* [Prometheus](https://fabiolb.net/feature/metrics/),
* [Circonus](https://fabiolb.net/feature/metrics/),
* [Graphite](https://fabiolb.net/feature/metrics/),
* [StatsD](https://fabiolb.net/feature/metrics/),
* [DataDog](https://fabiolb.net/feature/metrics/) for metrics,
* [WebUI](https://fabiolb.net/feature/web-ui/) and
* [Advertising BGP anycast addresses](https://fabiolb.net/feature/bgp/) on non-windows platforms.

[Watch](https://www.youtube.com/watch?v=gf43TcWjBrE&list=PL81sUbsFNc5b-Gd59Lpz7BW0eHJBt0GvE&index=1)
Kelsey Hightower demo Consul, Nomad, Vault and fabio at HashiConf EU 2016.

The full documentation is on [fabiolb.net](https://fabiolb.net/)

## Getting started

1. Install from source, [binary](https://github.com/fabiolb/fabio/releases),
   [Docker](https://hub.docker.com/r/fabiolb/fabio/) or [Homebrew](http://brew.sh).
    ```shell
	# go 1.15 or higher is required
    go install github.com/fabiolb/fabio@latest          (>= go1.15)

    brew install fabio                                  (OSX/macOS stable)
    brew install --devel fabio                          (OSX/macOS devel)

    docker pull fabiolb/fabio                           (Docker)

    https://github.com/fabiolb/fabio/releases           (pre-built binaries)
    ```

2. Register your service in [consul](https://consul.io/).

   Make sure that each instance registers with a **unique ServiceID** and a service name **without spaces**.

3. Register a **health check** in consul as described [here](https://consul.io/docs/agent/checks.html).

   By default fabio only watches services which have a **passing** health check, unless overridden with [registry.consul.service.status](https://fabiolb.net/ref/registry.consul.service.status/).

4. Register one `urlprefix-` tag per `host/path` prefix it serves, e.g.:

```
# HTTP/S examples
urlprefix-/css                                     # path route
urlprefix-i.com/static                             # host specific path route
urlprefix-mysite.com/                              # host specific catch all route
urlprefix-/foo/bar strip=/foo                      # path stripping (forward '/bar' to upstream)
urlprefix-/foo/bar proto=https                     # HTTPS upstream
urlprefix-/foo/bar proto=https tlsskipverify=true  # HTTPS upstream and self-signed cert

# TCP examples
urlprefix-:3306 proto=tcp                          # route external port 3306
```

   Make sure the prefix for HTTP routes contains **at least one slash** (`/`).

   See the full list of options in the [Documentation](https://github.com/fabiolb/fabio/wiki/Routing#config-language).

5. Start fabio without a config file (assuming a running consul agent on `localhost:8500`)
   Watch the log output how fabio picks up the route to your service.
   Try starting/stopping your service to see how the routing table changes instantly.

6. Send all your HTTP traffic to fabio on port `9999`.
   For TCP proxying see [TCP proxy](https://fabiolb.net/feature/tcp-proxy/).

7. Done

## Author and Founder

* Frank Schroeder [@magiconair](https://twitter.com/magiconair)

## Maintainers

* [Education Networks of America](https://github.com/myENA/)
* [Fabio Members](https://github.com/orgs/fabiolb/people)

### Contributors

This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].

## License

* Contributions up to 14 Apr 2017 before [38f73da](https://github.com/fabiolb/fabio/commit/38f73da6413b68fed1631101ac1d0b79a2fac870)

  MIT Licensed
  Copyright (c) 2017 eBay Software Foundation. All rights reserved.

* Contributions after 14 Apr 2017 starting with  [38f73da](https://github.com/fabiolb/fabio/commit/38f73da6413b68fed1631101ac1d0b79a2fac870)

  MIT Licensed
  Copyright (c) 2017-2019 Frank Schroeder. All rights reserved.

* Contributions after 22 Jan 2020 starting with [9da7b1b](https://github.com/fabiolb/fabio/commit/9da7b1b6ce0f631f7974e8663b34022c3496dca7#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5)

  MIT Licensed
  Copyright (c) 2020 Education Networks of America.  All rights reserved.

See [LICENSE](https://github.com/fabiolb/fabio/blob/master/LICENSE) for details.



================================================
FILE: admin/api/api.go
================================================
// Package api provides the HTTP api.
package api

import (
	"encoding/json"
	"log"
	"net/http"
)

func writeJSON(w http.ResponseWriter, r *http.Request, v interface{}) {
	_, pretty := r.URL.Query()["pretty"]

	var buf []byte
	var err error
	if pretty {
		buf, err = json.MarshalIndent(v, "", "    ")
	} else {
		buf, err = json.Marshal(v)
	}

	if err != nil {
		log.Print("[ERROR] ", err)
		http.Error(w, "internal error", 500)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(buf)
}


================================================
FILE: admin/api/config.go
================================================
package api

import "net/http"

type ConfigHandler struct {
	Config interface{}
}

func (h *ConfigHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, r, h.Config)
}


================================================
FILE: admin/api/manual.go
================================================
package api

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/fabiolb/fabio/registry"
)

// ManualHandler provides a fetch and update handler for the manual overrides api.
type ManualHandler struct {
	BasePath string
}

type manual struct {
	Value   string `json:"value"`
	Version uint64 `json:"version,string"`
}

func (h *ManualHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// we need this for testing.
	// under normal circumstances this is never nil
	if registry.Default == nil {
		return
	}

	path := r.RequestURI[len(h.BasePath):]

	switch r.Method {
	case "GET":
		value, version, err := registry.Default.ReadManual(path)
		if err != nil {
			log.Print("[ERROR] ", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		writeJSON(w, r, manual{value, version})
		return

	case "PUT":
		var m manual
		if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
			log.Print("[ERROR] ", err)
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		defer r.Body.Close()

		ok, err := registry.Default.WriteManual(path, m.Value, m.Version)
		if err != nil {
			log.Print("[ERROR] ", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		if !ok {
			http.Error(w, "version mismatch", http.StatusConflict)
			return
		}

	default:
		http.Error(w, "not allowed", http.StatusMethodNotAllowed)
	}
}


================================================
FILE: admin/api/paths.go
================================================
package api

import (
	"log"
	"net/http"
	"strings"

	"github.com/fabiolb/fabio/registry"
)

type ManualPathsHandler struct {
	Prefix string
}

func (h *ManualPathsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// we need this for testing.
	// under normal circumstances this is never nil
	if registry.Default == nil {
		return
	}

	switch r.Method {
	case "GET":
		paths, err := registry.Default.ManualPaths()
		if err != nil {
			log.Print("[ERROR] ", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		for i, p := range paths {
			paths[i] = strings.TrimPrefix(p, h.Prefix)
		}
		writeJSON(w, r, paths)
		return

	default:
		http.Error(w, "not allowed", http.StatusMethodNotAllowed)
	}
}


================================================
FILE: admin/api/routes.go
================================================
package api

import (
	"fmt"
	"net/http"
	"sort"
	"strings"

	"github.com/fabiolb/fabio/route"
)

type RoutesHandler struct{}

type apiRoute struct {
	Service string   `json:"service"`
	Host    string   `json:"host"`
	Path    string   `json:"path"`
	Src     string   `json:"src"`
	Dst     string   `json:"dst"`
	Opts    string   `json:"opts"`
	Cmd     string   `json:"cmd"`
	Tags    []string `json:"tags,omitempty"`
	Weight  float64  `json:"weight"`
	Rate1   float64  `json:"rate1"`
	Pct99   float64  `json:"pct99"`
}

func (h *RoutesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	t := route.GetTable()

	if _, ok := r.URL.Query()["raw"]; ok {
		w.Header().Set("Content-Type", "text/plain")
		fmt.Fprintln(w, t.String())
		return
	}

	var hosts []string
	for host := range t {
		hosts = append(hosts, host)
	}
	sort.Strings(hosts)

	var routes []apiRoute
	for _, host := range hosts {
		for _, tr := range t[host] {
			for _, tg := range tr.Targets {
				var opts []string
				for k, v := range tg.Opts {
					opts = append(opts, k+"="+v)
				}

				ar := apiRoute{
					Service: tg.Service,
					Host:    tr.Host,
					Path:    tr.Path,
					Src:     tr.Host + tr.Path,
					Dst:     tg.URL.String(),
					Opts:    strings.Join(opts, " "),
					Weight:  tg.Weight,
					Tags:    tg.Tags,
					Cmd:     "route add",
					// Rate1:   tg.Timer.Rate1(),
					// Pct99:   tg.Timer.Percentile(0.99),
				}
				routes = append(routes, ar)
			}
		}
	}
	writeJSON(w, r, routes)
}


================================================
FILE: admin/api/version.go
================================================
package api

import (
	"fmt"
	"net/http"
)

type VersionHandler struct {
	Version string
}

func (h *VersionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "%s", h.Version)
}


================================================
FILE: admin/server.go
================================================
package admin

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"strings"

	"github.com/fabiolb/fabio/admin/api"
	"github.com/fabiolb/fabio/admin/ui"
	"github.com/fabiolb/fabio/config"
	"github.com/fabiolb/fabio/proxy"
)

// Server provides the HTTP server for the admin UI and API.
type Server struct {
	Cfg      *config.Config
	Access   string
	Color    string
	Title    string
	Version  string
	Commands string
}

// ListenAndServe starts the admin server.
func (s *Server) ListenAndServe(l config.Listen, tlscfg *tls.Config) error {
	return proxy.ListenAndServeHTTP(l, s.handler(), tlscfg)
}

func (s *Server) handler() http.Handler {
	mux := http.NewServeMux()

	switch s.Access {
	case "ro":
		mux.HandleFunc("/api/paths", forbidden)
		mux.HandleFunc("/api/manual", forbidden)
		mux.HandleFunc("/api/manual/", forbidden)
		mux.HandleFunc("/manual", forbidden)
		mux.HandleFunc("/manual/", forbidden)
	case "rw":
		// for historical reasons the configured config path starts with a '/'
		// but Consul treats all KV paths without a leading slash.
		pathsPrefix := strings.TrimPrefix(s.Cfg.Registry.Consul.KVPath, "/")
		mux.Handle("/api/paths", &api.ManualPathsHandler{Prefix: pathsPrefix})
		mux.Handle("/api/manual", &api.ManualHandler{BasePath: "/api/manual"})
		mux.Handle("/api/manual/", &api.ManualHandler{BasePath: "/api/manual"})
		mux.Handle("/manual", &ui.ManualHandler{
			BasePath: "/manual",
			Color:    s.Color,
			Title:    s.Title,
			Version:  s.Version,
			Commands: s.Commands,
		})
		mux.Handle("/manual/", &ui.ManualHandler{
			BasePath: "/manual",
			Color:    s.Color,
			Title:    s.Title,
			Version:  s.Version,
			Commands: s.Commands,
		})
	}

	mux.Handle("/api/config", &api.ConfigHandler{Config: s.Cfg})
	mux.Handle("/api/routes", &api.RoutesHandler{})
	mux.Handle("/api/version", &api.VersionHandler{Version: s.Version})
	mux.Handle("/routes", &ui.RoutesHandler{Color: s.Color, Title: s.Title, Version: s.Version, RoutingTable: s.Cfg.UI.RoutingTable})
	mux.HandleFunc("/health", handleHealth)

	mux.Handle("/assets/", http.FileServer(http.FS(ui.Static)))
	mux.HandleFunc("/favicon.ico", http.NotFound)

	mux.Handle("/", http.RedirectHandler("/routes", http.StatusSeeOther))
	return mux
}

func handleHealth(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "OK")
}

func forbidden(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Forbidden", http.StatusForbidden)
}


================================================
FILE: admin/server_test.go
================================================
package admin

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/fabiolb/fabio/config"
)

func TestAdminServerAccess(t *testing.T) {
	type test struct {
		uri  string
		code int
	}

	testAccess := func(access string, tests []test) {
		srv := &Server{
			Access: access,
			Cfg: &config.Config{
				Registry: config.Registry{
					Consul: config.Consul{
						KVPath: "/fabio/config",
					},
				},
			},
		}
		ts := httptest.NewServer(srv.handler())
		defer ts.Close()

		noRedirectClient := &http.Client{
			CheckRedirect: func(req *http.Request, via []*http.Request) error {
				return http.ErrUseLastResponse
			},
		}
		for _, tt := range tests {
			t.Run(access+tt.uri, func(t *testing.T) {
				resp, err := noRedirectClient.Get(ts.URL + tt.uri)
				if err != nil {
					t.Fatalf("got %v want nil", err)
				}
				if got, want := resp.StatusCode, tt.code; got != want {
					t.Fatalf("got code %d want %d", got, want)
				}
			})
		}
	}

	roTests := []test{
		{"/api/manual", 403},
		{"/api/paths", 403},
		{"/api/config", 200},
		{"/api/routes", 200},
		{"/api/version", 200},
		{"/manual", 403},
		{"/routes", 200},
		{"/health", 200},
		{"/assets/logo.svg", 200},
		{"/assets/logo.bw.svg", 200},
		{"/", 303},
	}

	rwTests := []test{
		{"/api/manual", 200},
		{"/api/paths", 200},
		{"/api/config", 200},
		{"/api/routes", 200},
		{"/api/version", 200},
		{"/manual", 200},
		{"/routes", 200},
		{"/health", 200},
		{"/assets/logo.svg", 200},
		{"/assets/logo.bw.svg", 200},
		{"/", 303},
	}

	testAccess("ro", roTests)
	testAccess("rw", rwTests)
}


================================================
FILE: admin/ui/assets/fonts/material-icons.css
================================================
@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  src: url(MaterialIcons-Regular.eot); /* For IE6-8 */
  src: local('Material Icons'),
       local('MaterialIcons-Regular'),
       url(MaterialIcons-Regular.woff2) format('woff2'),
       url(MaterialIcons-Regular.woff) format('woff'),
       url(MaterialIcons-Regular.ttf) format('truetype');
}

.material-icons {
  font-family: 'Material Icons';
  font-weight: normal;
  font-style: normal;
  font-size: 24px;  /* Preferred icon size */
  display: inline-block;
  line-height: 1;
  text-transform: none;
  letter-spacing: normal;
  word-wrap: normal;
  white-space: nowrap;
  direction: ltr;

  /* Support for all WebKit browsers. */
  -webkit-font-smoothing: antialiased;
  /* Support for Safari and Chrome. */
  text-rendering: optimizeLegibility;

  /* Support for Firefox. */
  -moz-osx-font-smoothing: grayscale;

  /* Support for IE. */
  font-feature-settings: 'liga';
}


================================================
FILE: admin/ui/generate.go
================================================
package ui

//go:generate rm -rf assets/code.jquery.com
//go:generate rm -rf assets/cdnjs.cloudflare.com
//go:generate wget -pP assets https://code.jquery.com/jquery-3.6.0.min.js
//go:generate wget -pP assets https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js
//go:generate wget -pP assets https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css

// https://google.github.io/material-design-icons/#setup-method-2-self-hosting
//go:generate rm -rf assets/fonts
//go:generate wget -nH -nd -pP assets/fonts https://raw.githubusercontent.com/google/material-design-icons/3.0.1/iconfont/MaterialIcons-Regular.ttf
//go:generate wget -nH -nd -pP assets/fonts https://raw.githubusercontent.com/google/material-design-icons/3.0.1/iconfont/MaterialIcons-Regular.eot
//go:generate wget -nH -nd -pP assets/fonts https://raw.githubusercontent.com/google/material-design-icons/3.0.1/iconfont/MaterialIcons-Regular.woff
//go:generate wget -nH -nd -pP assets/fonts https://raw.githubusercontent.com/google/material-design-icons/3.0.1/iconfont/MaterialIcons-Regular.woff2
//go:generate wget -nH -nd -pP assets/fonts https://raw.githubusercontent.com/google/material-design-icons/3.0.1/iconfont/material-icons.css


================================================
FILE: admin/ui/manual.go
================================================
package ui

import (
	"html/template"
	"net/http"
)

type ManualHandler struct {
	BasePath string
	Color    string
	Title    string
	Version  string
	Commands string
}

func (h *ManualHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := r.RequestURI[len(h.BasePath):]
	data := struct {
		*ManualHandler
		Path    string
		APIPath string
	}{
		ManualHandler: h,
		Path:          path,
		APIPath:       "/api/manual" + path,
	}
	tmplManual.ExecuteTemplate(w, "manual", data)
}

var funcs = template.FuncMap{
	"noescape": func(str string) template.HTML {
		return template.HTML(str)
	},
}

var tmplManual = template.Must(template.New("manual").Funcs(funcs).Parse( // language=HTML
	`<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<title>fabio{{if .Title}} - {{.Title}}{{end}}</title>
	<script type="text/javascript" src="/assets/code.jquery.com/jquery-3.6.0.min.js"></script>
	<link href="/assets/fonts/material-icons.css" rel="stylesheet">
	<link rel="stylesheet" href="/assets/cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
	<script src="/assets/cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
	<meta name="viewport" content="width=device-width, initial-scale=1.0"/>

	<style type="text/css">
		.footer { padding-top: 10px; }
		.logo { height: 32px; margin: 0 auto; display: block; }
	</style>
</head>
<body>

<ul id="overrides" class="dropdown-content"></ul>

<nav class="top-nav {{.Color}}">

	<div class="container">
		<div class="nav-wrapper">
			<a href="/" class="brand-logo"><img alt="Fabio Logo" style="margin: 15px 0" class="logo" src="/assets/logo.bw.svg"> {{if .Title}} - {{.Title}}{{end}}</a>
			<ul id="nav-mobile" class="right hide-on-med-and-down">
				<li><a href="/routes">Routes</a></li>
				<li><a class="dropdown-trigger dropdown-button" href="#" data-target="overrides">Overrides<i class="material-icons right">arrow_drop_down</i></a></li>
				<li><a href="https://github.com/fabiolb/fabio/blob/master/CHANGELOG.md">{{.Version}}</a></li>
				<li><a href="https://github.com/fabiolb/fabio">Github</a></li>
				<li><a href="https://fabiolb.net">Fabiolb.net</a></li>
			</ul>
		</div>
	</div>

</nav>

<div class="container">

	<div class="section">
		<h5>Manual Routes{{if .Path}} for "{{.Path}}"{{end}}</h5>

		<div class="row">
			<form class="col s12">
				<input type="hidden" name="version">
				<div class="row">
					<div class="input-field col s12">
						<textarea id="textarea1" class="materialize-textarea"></textarea>
						<label for="textarea1"></label>
					</div>
				</div>
			</form>
			<button class="btn waves-effect waves-light" name="save">Save</button>
			<button class="btn waves-effect waves-light" name="help">Help</button>
		</div>

		<div class="row">
			<pre class="help hide">{{.Commands}}</pre>
		</div>
	</div>

</div>

<script>
$(function(){
	$('.dropdown-trigger').dropdown();
	let params={};window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(str,key,value){params[key] = value;});

	$.get({{.APIPath}}, function(data) {
		const $ta1 = $("#textarea1");
		$("input[name=version]").val(data.version);
		$("textarea>label").val("Version " + data.version);
		$ta1.val(data.value);
		M.textareaAutoResize($('#textarea1'));
	});

	$.get('/api/paths', function(data) {
		const d = $("#overrides");
		$.each(data, function(idx, val) {
			let path = val;
			if (val == "") {
				val = "default";
			}
			d.append(
				$('<li />').append(
					$('<a />').attr('href', '/manual'+path).text(val)
				)
			);
		});
	});

	$("button[name=help]").click(function() {
		$("pre.help").toggleClass("hide");
	});

	$("button[name=save]").click(function() {
		const data = {
			value   : $("#textarea1").val(),
			version : $("input[name=version]").val()
		}
		$.ajax({{.APIPath}}, {
			type: 'PUT',
			data: JSON.stringify(data),
			contentType: 'application/json',
			statusCode: {
				400: function(jqXHR, textStatus, err) { alert(err); },
				409: function(jqXHR, textStatus, err) { alert(err); },
				500: function(jqXHR, textStatus, err) { alert(err); }
			},
			success: function() {
				window.location.reload();
			}
		});
	});
})
</script>

</body>
</html>
`))


================================================
FILE: admin/ui/route.go
================================================
package ui

import (
	"html/template"
	"net/http"

	"github.com/fabiolb/fabio/config"
)

// RoutesHandler provides the UI for managing the routing table.
type RoutesHandler struct {
	Color        string
	Title        string
	Version      string
	RoutingTable config.RoutingTable
}

func (h *RoutesHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
	tmplRoutes.ExecuteTemplate(w, "routes", h)
}

var tmplRoutes = template.Must(template.New("routes").Parse( // language=HTML
	`<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<title>fabio{{if .Title}} - {{.Title}}{{end}}</title>
	<script type="text/javascript" src="/assets/code.jquery.com/jquery-3.6.0.min.js"></script>
	<link href="/assets/fonts/material-icons.css" rel="stylesheet">
	<link rel="stylesheet" href="/assets/cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
	<script src="/assets/cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
	<meta name="viewport" content="width=device-width, initial-scale=1.0"/>

	<style type="text/css">
		td.tags { display: none; }
		.footer { padding-top: 10px; }
		.logo { height: 32px; margin: 0 auto; display: block; }

		@media (min-width: 78em) {
			td.tags{ display: table-cell; }
		}

		.tooltip { position: relative; display: inline-block; }
		.tooltip .tooltiptext {
			visibility: hidden;
			width: 250px;
			background-color: #000000;
			color: #ffffff;
			text-align: center;
			margin: 30px 0px 0px 30px;
			padding: 5px 0;
			border-radius: 6px;
			position: absolute;
			z-index: 1000;
		}
		.tooltip:hover .tooltiptext { visibility: visible; }
	</style>
</head>
<body>

<ul id="overrides" class="dropdown-content"></ul>

<nav class="top-nav {{.Color}}">

	<div class="container">
		<div class="nav-wrapper">
			<a href="/" class="brand-logo"><img alt="Fabio Logo" style="margin: 15px 0" class="logo" src="/assets/logo.bw.svg"> {{if .Title}} - {{.Title}}{{end}}</a>
			<ul id="nav-mobile" class="right hide-on-med-and-down">
				<li><a class="dropdown-trigger dropdown-button" href="#" data-target="overrides">Overrides<i class="material-icons right">arrow_drop_down</i></a></li>
				<li><a href="https://github.com/fabiolb/fabio/blob/master/CHANGELOG.md">{{.Version}}</a></li>
				<li><a href="https://github.com/fabiolb/fabio">Github</a></li>
				<li><a href="https://fabiolb.net">Fabiolb.net</a></li>
			</ul>
		</div>
	</div>

</nav>

<div class="container">

	<div class="section">
		<h5>Routing Table</h5>
		<p><input type="text" id="filter" placeholder="type to filter routes"></p>
		<table class="routes highlight"></table>
	</div>

	<div class="section footer">
		<img alt="Fabio Logo" class="logo" src="/assets/logo.svg">
	</div>

</div>

<script>
$(function(){
	let params={};window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(str,key,value){params[key] = value;});

	$('.dropdown-trigger').dropdown();
	function renderRoutes(routes) {
		const $table = $('table.routes');

		let thead = '<thead><tr>';
		thead += '<th>#</th>';
		thead += '<th>Service</th>';
		thead += '<th>Source</th>';
		thead += '<th>Dest</th>';
		thead += '<th>Options</th>';
		thead += '<th>Weight</th>';
		thead += '</tr></thead>';

		let $tbody = $('<tbody />');

		console.log(routes);

		if (routes != null) {
			for (let i=0; i < routes.length; i++) {
				const r = routes[i];
				
				let $tr = $('<tr />');
				if (/^https?:\/+/i.exec(r.src) != null) { 
					$tr = $('<tr />').attr('style', 'background-color: #ff1a1a;');
					$tr.append($('<td />').addClass('tooltip').append($('<span class="tooltiptext"></span>').text("Route Source cannot start with the protocol or scheme (e.g. - 'http' and 'https' are invalid to have listed in the route source)")).append($('<span class="valign-wrapper" />').append(i+1).append($('<i class="material-icons">error_outline</i>'))));
				} else {
					$tr.append($('<td />').text(i+1));
				}
				$tr.append($('<td />').text(r.service));

				if ({{.RoutingTable.Source.LinkEnabled}} == true && /^https?:\/+/i.exec(r.dst) != null && /^https?:\/+/i.exec(r.src) == null) {
					const hrefScheme = ({{.RoutingTable.Source.Scheme}} != '' ? {{.RoutingTable.Source.Scheme}} + ':' : window.location.protocol) + '//';
					const hrefHost = ({{.RoutingTable.Source.Host}} != '' ? {{.RoutingTable.Source.Host}} : window.location.hostname);
					const hrefPort = (/:/gi.exec(r.src) != null ? /:[0-9]*\/?/gi.exec(r.src)[0] : '{{if .RoutingTable.Source.Port}}:{{.RoutingTable.Source.Port}}{{end}}');
					const hrefStr = (r.src.startsWith('/') ? hrefScheme + hrefHost + hrefPort : '{{if .RoutingTable.Source.Scheme}}{{.RoutingTable.Source.Scheme}}:{{end}}//') + r.src;
					$tr.append($('<td />').append($('<a />').attr('href', hrefStr){{if .RoutingTable.Source.NewTab}}.attr('target', '_blank'){{end}}.text(r.src)));
				} else {
					$tr.append($('<td />').text(r.src));
				}

				$tr.append($('<td />').append($('<a />').attr('href', r.dst).text(r.dst)));
				$tr.append($('<td />').text(r.opts));
				$tr.append($('<td />').text((r.weight * 100).toFixed(2) + '%'));

				$tr.appendTo($tbody);
			}
		}

		$table.empty().
			append($(thead)).
			append($tbody);
	}

	let $filter = $('#filter');
	function doFilter(v) {
		$("tr").show();
		if (!v) return;
		let words = v.split(' ');
		for (let i=0; i < words.length; i++) {
			let w = words[i].trim();
			if (w === "") continue;
			$("tbody tr:not(:contains('"+w+"'))").hide();
		}
	}

	$filter.focus();
	$filter.keyup(function() {
		const v = $filter.val();
		window.history.pushState(null, null, "?filter=" +v);
		doFilter(v);
	});

	$.get("/api/routes", function(data) {
		renderRoutes(data);
		if (!params.filter) return;
		const v = decodeURIComponent(params.filter);
		$filter.val(v);
		doFilter(v);
	});

	$.get('/api/paths', function(data) {
		const d = $("#overrides");
		$.each(data, function(idx, val) {
			let path = val;
			if (val == "") {
				val = "default";
			}
			d.append(
				$('<li />').append(
					$('<a />').attr('href', '/manual'+path).text(val)
				)
			);
		});
	});
});
</script>

</body>
</html>
`))


================================================
FILE: admin/ui/static.go
================================================
package ui

import "embed"

//go:embed assets/*
var Static embed.FS


================================================
FILE: assert/assert.go
================================================
// Package assert provides a simple assert framework.
package assert

import (
	"fmt"
	"path/filepath"
	"reflect"
	"runtime"
	"testing"
)

// Equal provides an assertEqual function
func Equal(t *testing.T) func(got, want interface{}) {
	return EqualDepth(t, 1, "")
}

func EqualDepth(t *testing.T, calldepth int, desc string) func(got, want interface{}) {
	return func(got, want interface{}) {
		_, file, line, _ := runtime.Caller(calldepth)
		if !reflect.DeepEqual(got, want) {
			fmt.Printf("\t%s:%d: %s: got %v want %v\n", filepath.Base(file), line, desc, got, want)
			t.Fail()
		}
	}
}


================================================
FILE: auth/auth.go
================================================
package auth

import (
	"fmt"
	"net/http"

	"github.com/fabiolb/fabio/config"
)

type AuthScheme interface {
	Authorized(request *http.Request, response http.ResponseWriter) bool
}

func LoadAuthSchemes(cfg map[string]config.AuthScheme) (map[string]AuthScheme, error) {
	auths := map[string]AuthScheme{}
	for _, a := range cfg {
		switch a.Type {
		case "basic":
			b, err := newBasicAuth(a.Basic)
			if err != nil {
				return nil, err
			}
			auths[a.Name] = b
		default:
			return nil, fmt.Errorf("unknown auth type '%s'", a.Type)
		}
	}

	return auths, nil
}


================================================
FILE: auth/auth_test.go
================================================
package auth

import (
	"testing"

	"github.com/fabiolb/fabio/config"
)

func TestLoadAuthSchemes(t *testing.T) {

	t.Run("should fail when auth scheme fails to load", func(t *testing.T) {
		_, err := LoadAuthSchemes(map[string]config.AuthScheme{
			"myauth": {
				Name: "myauth",
				Type: "basic",
				Basic: config.BasicAuth{
					File: "/some/non/existent/file",
				},
			},
		})

		const errorText = "open /some/non/existent/file: no such file or directory"

		if err.Error() != errorText {
			t.Fatalf("got %s, want %s", err.Error(), errorText)
		}
	})

	t.Run("should return an error when auth type is unknown", func(t *testing.T) {
		_, err := LoadAuthSchemes(map[string]config.AuthScheme{
			"myauth": {
				Name: "myauth",
				Type: "foo",
			},
		})

		const errorText = "unknown auth type 'foo'"

		if err.Error() != errorText {
			t.Fatalf("got %s, want %s", err.Error(), errorText)
		}
	})

	t.Run("should load multiple auth schemes", func(t *testing.T) {
		myauth, err := createBasicAuthFile("foo:bar", t)
		if err != nil {
			t.Fatalf("could not create file on disk %s", err)
		}

		myotherauth, err := createBasicAuthFile("bar:foo", t)
		if err != nil {
			t.Fatalf("could not create file on disk %s", err)
		}

		result, _ := LoadAuthSchemes(map[string]config.AuthScheme{
			"myauth": {
				Name: "myauth",
				Type: "basic",
				Basic: config.BasicAuth{
					File: myauth,
				},
			},
			"myotherauth": {
				Name: "myotherauth",
				Type: "basic",
				Basic: config.BasicAuth{
					File: myotherauth,
				},
			},
		})

		if len(result) != 2 {
			t.Fatalf("expected 2 auth schemes, got %d", len(result))
		}
	})
}


================================================
FILE: auth/basic.go
================================================
package auth

import (
	"bytes"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/fabiolb/fabio/config"
	"github.com/tg123/go-htpasswd"
)

// basic is an implementation of AuthScheme
type basic struct {
	secrets *htpasswd.File
	realm   string
}

func newBasicAuth(cfg config.BasicAuth) (AuthScheme, error) {
	bad := func(err error) {
		log.Println("[WARN] Error processing a line in an htpasswd file:", err)
	}

	secrets, err := htpasswd.New(cfg.File, htpasswd.DefaultSystems, bad)
	if err != nil {
		return nil, err
	}

	if cfg.Refresh > 0 {
		stat, err := os.Stat(cfg.File)
		if err != nil {
			return nil, err
		}
		cfg.ModTime = stat.ModTime()

		go func() {
			cleared := false
			ticker := time.NewTicker(cfg.Refresh).C
			for range ticker {
				stat, err := os.Stat(cfg.File)
				if err != nil {
					log.Println("[WARN] Error accessing htpasswd file:", err)
					if !cleared {
						err = secrets.ReloadFromReader(&bytes.Buffer{}, bad)
						if err != nil {
							log.Println("[WARN] Error clearing the htpasswd credentials:", err)
						} else {
							log.Println("[INFO] The htpasswd credentials have been cleared")
							cleared = true
						}
					}
					continue
				}

				// refresh the htpasswd file only if its modification time has changed
				// even if the new htpasswd file is older than previously loaded
				if cfg.ModTime != stat.ModTime() {
					if err := secrets.Reload(bad); err == nil {
						log.Println("[INFO] The htpasswd file has been successfully reloaded")
						cfg.ModTime = stat.ModTime()
						cleared = false
					} else {
						log.Println("[WARN] Error reloading htpasswd file:", err)
					}
				}
			}
		}()
	}

	return &basic{
		secrets: secrets,
		realm:   cfg.Realm,
	}, nil
}

func (b *basic) Authorized(request *http.Request, response http.ResponseWriter) bool {
	user, password, ok := request.BasicAuth()

	if !ok {
		response.Header().Set("WWW-Authenticate", "Basic realm=\""+b.realm+"\"")
		return false
	}

	return b.secrets.Match(user, password)
}


================================================
FILE: auth/basic_test.go
================================================
package auth

import (
	"encoding/base64"
	"fmt"
	"net/http"
	"os"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/fabiolb/fabio/config"
	"github.com/fabiolb/fabio/uuid"
)

type responseWriter struct {
	header  http.Header
	code    int
	written []byte
}

func (rw *responseWriter) Header() http.Header {
	if rw.header == nil {
		rw.header = map[string][]string{}
	}
	return rw.header
}

func (rw *responseWriter) Write(b []byte) (int, error) {
	rw.written = append(rw.written, b...)
	return len(rw.written), nil
}

func (rw *responseWriter) WriteHeader(statusCode int) {
	rw.code = statusCode
}

func createBasicAuthFile(contents string, t *testing.T) (string, error) {
	dir := t.TempDir()

	filename := fmt.Sprintf("%s/%s", dir, uuid.NewUUID())

	err := os.WriteFile(filename, []byte(contents), 0666)

	if err != nil {
		return "", fmt.Errorf("could not write password file: %s", err)
	}

	return filename, nil
}

func createBasicAuth(user string, password string, t *testing.T) (AuthScheme, error) {
	contents := fmt.Sprintf("%s:%s", user, password)

	filename, err := createBasicAuthFile(contents, t)
	if err != nil {
		return nil, fmt.Errorf("could not create basic auth: %s", err)
	}

	a, err := newBasicAuth(config.BasicAuth{
		File:  filename,
		Realm: "testrealm",
	})

	if err != nil {
		return nil, fmt.Errorf("could not create basic auth: %s", err)
	}

	return a, nil
}

func TestNewBasicAuth(t *testing.T) {

	t.Run("should create a basic auth scheme from the supplied config", func(t *testing.T) {
		filename, err := createBasicAuthFile("foo:bar", t)

		if err != nil {
			t.Error(err)
		}

		_, err = newBasicAuth(config.BasicAuth{
			File: filename,
		})

		if err != nil {
			t.Error(err)
		}
	})

	t.Run("should log a warning when credentials are malformed", func(t *testing.T) {
		filename, err := createBasicAuthFile("foosdlijdgohdgdbar", t)

		if err != nil {
			t.Error(err)
		}

		_, err = newBasicAuth(config.BasicAuth{
			File: filename,
		})

		if err != nil {
			t.Error(err)
		}
	})
}

func TestBasic_Authorised(t *testing.T) {
	basicAuth, err := createBasicAuth("foo", "bar", t)
	creds := []byte("foo:bar")

	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		name string
		req  *http.Request
		res  http.ResponseWriter
		out  bool
	}{
		{
			"correct credentials should be authorized",
			&http.Request{
				Header: http.Header{
					"Authorization": []string{fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString(creds))},
				},
			},
			&responseWriter{},
			true,
		},
		{
			"incorrect credentials should not be authorized",
			&http.Request{
				Header: http.Header{
					"Authorization": []string{fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte("baz:blarg")))},
				},
			},
			&responseWriter{},
			false,
		},
		{
			"missing Authorization header should not be authorized",
			&http.Request{
				Header: http.Header{},
			},
			&responseWriter{},
			false,
		},
		{
			"malformed Authorization header should not be authorized",
			&http.Request{
				Header: http.Header{
					"Authorization": []string{"malformed"},
				},
			},
			&responseWriter{},
			false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got, want := basicAuth.Authorized(tt.req, tt.res), tt.out; !reflect.DeepEqual(got, want) {
				t.Errorf("got %v want %v", got, want)
			}
		})
	}
}

func TestBasic_Authorised_should_fail_without_htpasswd_file(t *testing.T) {
	filename, err := createBasicAuthFile("foo:bar", t)
	if err != nil {
		t.Error(err)
	}

	a, err := newBasicAuth(config.BasicAuth{
		File:    filename,
		Refresh: time.Second,
	})
	if err != nil {
		t.Error(err)
	}

	creds := []byte("foo:bar")
	r := &http.Request{
		Header: http.Header{
			"Authorization": []string{fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString(creds))},
		},
	}

	w := &responseWriter{}

	t.Run("should authorize against supplied htpasswd file", func(t *testing.T) {
		if got, want := a.Authorized(r, w), true; !reflect.DeepEqual(got, want) {
			t.Errorf("got %v want %v", got, want)
		}
	})

	if err := os.Remove(filename); err != nil {
		t.Fatalf("removing htpasswd file: %s", err)
	}

	time.Sleep(2 * time.Second) // ensure htpasswd file refresh happened

	t.Run("should not authorize after removing htpasswd file", func(t *testing.T) {
		if got, want := a.Authorized(r, w), false; !reflect.DeepEqual(got, want) {
			t.Errorf("got %v want %v", got, want)
		}
	})
}

func TestBasic_Authorized_should_set_www_realm_header(t *testing.T) {
	basicAuth, err := createBasicAuth("foo", "bar", t)

	if err != nil {
		t.Fatal(err)
	}

	rw := &responseWriter{}

	_ = basicAuth.Authorized(&http.Request{Header: http.Header{}}, rw)

	got := rw.Header().Get("WWW-Authenticate")
	want := `Basic realm="testrealm"`

	if strings.Compare(got, want) != 0 {
		t.Errorf("got '%s', want '%s'", got, want)
	}
}


================================================
FILE: bgp/bgp_nonwindows.go
================================================
//go:build !windows
// +build !windows

package bgp

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net"
	"os"

	"github.com/fabiolb/fabio/config"
	"github.com/fabiolb/fabio/exit"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/protobuf/proto"
	apb "google.golang.org/protobuf/types/known/anypb"

	api "github.com/osrg/gobgp/v3/api"
	bgpconfig "github.com/osrg/gobgp/v3/pkg/config"
	"github.com/osrg/gobgp/v3/pkg/server"
)

var (
	ErrMissingAnycast  = errors.New("you must specify at least one anycast address to advertise")
	ErrMissingPeers    = errors.New("you must specify at least one peer to advertise routes to")
	ErrMissingRouterID = errors.New("you must specify the routerID of this host, i.e. a non anycast address")
	ErrNoRoutesAdded   = errors.New("no routes were successfully added")
	ErrNoRoutesDeleted = errors.New("no routes were successfully deleted")
	ErrNoPeersAdded    = errors.New("no peers were successfully added")
)

const (
	denyAllNeighbors = "deny-all-neighbors"
	matchAnyPeer     = "match-any-peer"
	globalTable      = "global"
	rejectAll        = "reject-all"
)

type BGPHandler struct {
	server     *server.BgpServer
	config     *config.BGP
	routeAttrs []*apb.Any
}

func NewBGPHandler(config *config.BGP) (*BGPHandler, error) {
	// pre-chew some protobuf messages that are part of
	// every anycast route we'll be adding.
	nextHop := config.RouterID
	if len(config.NextHop) > 0 {
		nextHop = config.NextHop
	}
	var messages = []proto.Message{
		&api.OriginAttribute{
			Origin: 0,
		},
		&api.NextHopAttribute{
			NextHop: nextHop,
		},
		&api.AsPathAttribute{
			Segments: []*api.AsSegment{
				{
					Type:    api.AsSegment_AS_SEQUENCE,
					Numbers: []uint32{uint32(config.Asn)},
				},
			},
		},
	}
	attributes := make([]*apb.Any, 0, len(messages))
	for _, p := range messages {
		attr, err := apb.New(p)
		if err != nil {
			// should never happen
			panic(err)
		}
		attributes = append(attributes, attr)
	}

	var opts = []server.ServerOption{server.LoggerOption(bgpLogger{})}
	if config.EnableGRPC {
		maxSize := 256 << 20
		grpcOpts := []grpc.ServerOption{grpc.MaxRecvMsgSize(maxSize), grpc.MaxSendMsgSize(maxSize)}
		if config.GRPCTLS {
			creds, err := credentials.NewServerTLSFromFile(config.CertFile, config.KeyFile)
			if err != nil {
				// shouldn't get here if validate was called first.
				return nil, fmt.Errorf("error parsing bgp TLS credentials: %s", err)
			}
			grpcOpts = append(grpcOpts, grpc.Creds(creds))
		}
		opts = append(opts,
			server.GrpcOption(grpcOpts),
			server.GrpcListenAddress(config.GRPCListenAddress),
		)
	}
	return &BGPHandler{
		server:     server.NewBgpServer(opts...),
		config:     config,
		routeAttrs: attributes,
	}, nil
}

func (bgph *BGPHandler) Start() error {
	s := bgph.server
	go s.Serve()

	if len(bgph.config.GOBGPDCfgFile) > 0 {
		initialCfg, err := bgpconfig.ReadConfigFile(bgph.config.GOBGPDCfgFile, "toml")
		if err != nil {
			// shouldn't happen if we called validate first.
			return err
		}
		_, err = bgpconfig.InitialConfig(context.Background(), s, initialCfg, false)
		if err != nil {
			return fmt.Errorf("bgp: error initializing from gobgp config: %w", err)
		}
	} else {
		// If we weren't passed a gobgp config file, configure using the values passed from the fabio
		// config, and make sure we have a sane policy where we export our routes to peers but don't
		// import from any peers.
		err := bgph.startBGP(context.Background())
		if err != nil {
			return fmt.Errorf("bgp: error starting: %w", err)
		}

		err = bgph.setPolicies()
		if err != nil {
			return fmt.Errorf("bgp error setting policy: %w", err)
		}

	}

	errCh := make(chan error, 1)
	exit.Listen(func(sig os.Signal) {
		log.Printf("[INFO] Stopping BGP")
		err := s.StopBgp(context.Background(), &api.StopBgpRequest{})
		errCh <- err
	})

	// monitor the change of the peer state
	if err := s.WatchEvent(context.Background(), &api.WatchEventRequest{Peer: &api.WatchEventRequest_Peer{}}, func(r *api.WatchEventResponse) {
		if p := r.GetPeer(); p != nil && p.Type == api.WatchEventResponse_PeerEvent_STATE {
			log.Printf("[DEBUG] bgp event: %#v", p)
		}
	}); err != nil {
		log.Printf("[ERROR] bgp watcher failed: %s", err)
	}
	if len(bgph.config.GOBGPDCfgFile) == 0 || len(bgph.config.Peers) > 0 {
		// add peers
		err := bgph.addNeighbors(context.Background(), bgph.config.Peers)
		if err != nil {
			return fmt.Errorf("bgp error adding neighbors: %w", err)
		}
	}
	if len(bgph.config.AnycastAddresses) > 0 {
		err := bgph.AddRoutes(context.Background(), bgph.config.AnycastAddresses)
		if err != nil {
			return fmt.Errorf("bgp error adding anycastaddresses: %w", err)
		}
	}
	// hang until exit handler completes above.
	return <-errCh
}

func (bgph *BGPHandler) startBGP(ctx context.Context) error {
	return bgph.server.StartBgp(ctx, &api.StartBgpRequest{
		Global: &api.Global{
			Asn:             uint32(bgph.config.Asn),
			RouterId:        bgph.config.RouterID,
			ListenPort:      int32(bgph.config.ListenPort),
			ListenAddresses: bgph.config.ListenAddresses,
		},
	})
}

func (bgph *BGPHandler) setPolicies() error {
	// Create a policy that denies all routes from any neighbor.
	err := bgph.server.SetPolicies(context.Background(), &api.SetPoliciesRequest{
		DefinedSets: []*api.DefinedSet{
			{
				DefinedType: api.DefinedType_NEIGHBOR,
				Name:        matchAnyPeer,
				List:        []string{"0.0.0.0/0", "::/0"},
			},
		},
		Policies: []*api.Policy{
			{
				Name: denyAllNeighbors,
				Statements: []*api.Statement{
					{
						Name: rejectAll,
						Conditions: &api.Conditions{
							NeighborSet: &api.MatchSet{
								Name: matchAnyPeer,
							},
						},
						Actions: &api.Actions{
							RouteAction: api.RouteAction_REJECT,
						},
					},
				},
			},
		},
	})
	if err != nil {
		return err
	}

	// Assign the above to the global policy
	return bgph.server.SetPolicyAssignment(context.Background(), &api.SetPolicyAssignmentRequest{
		Assignment: &api.PolicyAssignment{
			Name:      globalTable, // this is the global rib
			Direction: api.PolicyDirection_IMPORT,
			Policies: []*api.Policy{
				{
					Name: denyAllNeighbors,
				},
			},
			// Need to set default action to accept here because otherwise
			// even routes added via API calls get rejected.
			DefaultAction: api.RouteAction_ACCEPT,
		},
	})
}

func (bgph *BGPHandler) addNeighbors(ctx context.Context, peers []config.BGPPeer) error {
	var errs []error
	peerCount := 0
	for _, peer := range peers {
		var hop *api.EbgpMultihop
		if peer.MultiHop {
			hop = &api.EbgpMultihop{
				Enabled:     true,
				MultihopTtl: uint32(peer.MultiHopLength),
			}
		}
		var trans *api.Transport
		if peer.NeighborPort > 0 {
			trans = &api.Transport{
				LocalAddress:  bgph.config.RouterID,
				MtuDiscovery:  false,
				PassiveMode:   false,
				RemoteAddress: peer.NeighborAddress,
				RemotePort:    uint32(peer.NeighborPort),
				TcpMss:        0,
				BindInterface: "",
			}
		}
		err := bgph.server.AddPeer(ctx, &api.AddPeerRequest{
			Peer: &api.Peer{
				Conf: &api.PeerConf{
					AuthPassword:    peer.Password,
					NeighborAddress: peer.NeighborAddress,
					PeerAsn:         uint32(peer.Asn),
				},
				EbgpMultihop: hop,
				Transport:    trans,
			},
		})
		if err != nil {
			errs = append(errs, err)
			continue
		}
		peerCount++
	}
	if peerCount == 0 {
		errs = append(errs, ErrNoPeersAdded)
	}
	return errors.Join(errs...)
}

func (bgph *BGPHandler) AddRoutes(ctx context.Context, routes []string) error {
	var errs []error
	// Add our Anycast routes

	routesAdded := 0

	for _, addr := range routes {
		_, ipnet, err := net.ParseCIDR(addr)
		if err != nil {
			errs = append(errs, err)
			continue
		}
		prefixLen, _ := ipnet.Mask.Size()
		af := api.Family_AFI_IP
		if ipnet.IP.To4() == nil {
			af = api.Family_AFI_IP6
		}
		nlri, _ := apb.New(&api.IPAddressPrefix{
			PrefixLen: uint32(prefixLen),
			Prefix:    ipnet.IP.String(),
		})
		_, err = bgph.server.AddPath(ctx, &api.AddPathRequest{
			Path: &api.Path{
				Nlri:   nlri,
				Pattrs: bgph.routeAttrs,
				Family: &api.Family{
					Afi:  af,
					Safi: api.Family_SAFI_UNICAST,
				},
			},
		})
		if err != nil {
			log.Printf("[ERROR] bgp error adding path for %s: %s", addr, err)
			errs = append(errs, fmt.Errorf("error adding %s: %w", addr, err))
		} else {
			log.Printf("[INFO] bgp successfully added path for %s", addr)
			routesAdded++
		}
	}
	if routesAdded == 0 {
		errs = append(errs, ErrNoRoutesAdded)
	}
	return errors.Join(errs...)
}

func (bgph *BGPHandler) DeleteRoutes(ctx context.Context, routes []string) error {
	var errs []error
	delCount := 0
	for _, addr := range routes {
		_, ipnet, err := net.ParseCIDR(addr)
		if err != nil {
			errs = append(errs, err)
			continue
		}
		prefixLen, _ := ipnet.Mask.Size()
		af := api.Family_AFI_IP
		if ipnet.IP.To4() == nil {
			af = api.Family_AFI_IP6
		}
		nlri, _ := apb.New(&api.IPAddressPrefix{
			PrefixLen: uint32(prefixLen),
			Prefix:    ipnet.IP.String(),
		})
		err = bgph.server.DeletePath(ctx, &api.DeletePathRequest{
			TableType: api.TableType_GLOBAL,
			Path: &api.Path{
				Nlri: nlri,
				Family: &api.Family{
					Afi:  af,
					Safi: api.Family_SAFI_UNICAST,
				},
				Pattrs: bgph.routeAttrs,
			},
		})
		if err != nil {
			errs = append(errs, err)
			continue
		}
		delCount++
	}
	if delCount == 0 {
		errs = append(errs, ErrNoRoutesDeleted)
	}
	return errors.Join(errs...)
}

func ValidateConfig(config *config.BGP) error {
	if !config.BGPEnabled {
		return nil
	}

	for _, addr := range config.AnycastAddresses {
		_, _, err := net.ParseCIDR(addr)
		if err != nil {
			return fmt.Errorf("could not parse cidr for anycast address %s: %w", addr, err)
		}
	}

	if config.EnableGRPC && config.GRPCTLS {
		_, err := credentials.NewServerTLSFromFile(config.CertFile, config.KeyFile)
		if err != nil {
			return fmt.Errorf("could not parse bgp tls credentials: %w", err)
		}
	}

	for _, peer := range config.Peers {
		if net.ParseIP(peer.NeighborAddress) == nil {
			return fmt.Errorf("peer address %s is not a valid IP", peer.NeighborAddress)
		}
	}

	if len(config.GOBGPDCfgFile) > 0 {
		_, err := bgpconfig.ReadConfigFile(config.GOBGPDCfgFile, "toml")
		if err != nil {
			return fmt.Errorf("could not open %s: %w", config.GOBGPDCfgFile, err)
		}
		// otherwise we skip the rest of these checks, hopefully the provided bobgpd config is sane.
		return nil
	}

	if len(config.AnycastAddresses) == 0 {
		return ErrMissingAnycast
	}
	if len(config.Peers) == 0 {
		return ErrMissingPeers
	}

	if len(config.RouterID) == 0 {
		return ErrMissingRouterID
	}
	if net.ParseIP(config.RouterID) == nil {
		return fmt.Errorf("router ID %s is not a valid ID", config.RouterID)
	}
	if len(config.NextHop) > 0 {
		if ip := net.ParseIP(config.NextHop); ip == nil {
			return fmt.Errorf("invalid NextHop: %s", config.NextHop)
		}
	}
	return nil
}


================================================
FILE: bgp/bgp_nonwindows_test.go
================================================
//go:build !windows
// +build !windows

package bgp

import (
	"context"
	"encoding/json"
	"github.com/fabiolb/fabio/config"
	api "github.com/osrg/gobgp/v3/api"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
	"time"
)

func TestBGPHandler(t *testing.T) {
	serverCmd := &gobgpserver{
		cmdPath: "gobgpd",
	}
	err := serverCmd.start()
	if err != nil {
		t.Logf("error calling gobgpd command, probably not installed. skipping: %s", err)
		t.SkipNow()
	}
	defer serverCmd.stop()
	cfg := &config.BGP{
		BGPEnabled:       true,
		Asn:              65000,
		AnycastAddresses: []string{"1.2.3.4/32"},
		RouterID:         "127.0.0.2",
		ListenPort:       1790,
		ListenAddresses:  []string{"127.0.0.2"},
		Peers: []config.BGPPeer{
			{
				NeighborAddress: "127.0.0.3",
				NeighborPort:    1790,
				Asn:             65001,
				MultiHop:        false,
			},
		},
		EnableGRPC:        true,
		GRPCListenAddress: "127.0.0.2:50051",
		NextHop:           "1.2.3.4",
	}
	bh, err := NewBGPHandler(cfg)
	if err != nil {
		t.Fatal(err)
	}
	go bh.server.Serve()
	defer bh.server.Stop()
	err = bh.startBGP(context.Background())
	if err != nil {
		t.Fatalf("error starting BGP: %s", err)
	}
	err = bh.addNeighbors(context.Background(), cfg.Peers)
	if err != nil {
		t.Fatalf("error adding neighbors: %s", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*60)
	defer cancel()
	if err := bh.server.WatchEvent(context.Background(), &api.WatchEventRequest{Peer: &api.WatchEventRequest_Peer{}}, func(r *api.WatchEventResponse) {
		if p := r.GetPeer(); p != nil && p.Type == api.WatchEventResponse_PeerEvent_STATE {
			t.Logf("EVENT RECEIVED %#v", p.Peer)
			if p.Peer.State.SessionState == api.PeerState_ESTABLISHED {
				cancel()
			}
		}
	}); err != nil {
		t.Fatal(err)
	}

	<-ctx.Done()
	if ctx.Err() == context.DeadlineExceeded {
		t.Fatal("context deadline exceeded")
	}

	gc := gobpgclient{
		cmdPath:  "gobgp",
		hostAddr: "127.0.0.3",
	}

	// now start a test table

	for _, tst := range []struct {
		name      string
		cmd       func() error
		routeKeys []string
	}{
		{
			name: "test add route",
			cmd: func() error {
				return bh.AddRoutes(context.Background(), cfg.AnycastAddresses)
			},
			routeKeys: []string{"1.2.3.4/32"},
		},
		{
			name: "test delete route",
			cmd: func() error {
				return bh.DeleteRoutes(context.Background(), []string{"1.2.3.4/32"})
			},
			routeKeys: nil,
		},
	} {
		t.Run(tst.name, func(t *testing.T) {
			err := tst.cmd()
			if err != nil {
				t.Fatal(err)
			}
			routes, err := gc.globalRib(t)
			if err != nil {
				t.Fatal(err)
			}
			if len(routes) != len(tst.routeKeys) {
				t.Fatalf("routes don't match, have %d want %d",
					len(routes), len(tst.routeKeys))
			}
			for _, r := range tst.routeKeys {
				if _, ok := routes[r]; !ok {
					t.Fatalf("route %s not found", r)
				}
			}
		})
	}

}

type ribEntry struct {
	Nlri struct {
		Prefix string `json:"prefix"`
	} `json:"nlri"`
	Age   int  `json:"age"`
	Best  bool `json:"best"`
	Attrs []struct {
		Type    int `json:"type"`
		Value   int `json:"value,omitempty"`
		AsPaths []struct {
			SegmentType int   `json:"segment_type"`
			Num         int   `json:"num"`
			Asns        []int `json:"asns"`
		} `json:"as_paths,omitempty"`
		Nexthop string `json:"nexthop,omitempty"`
	} `json:"attrs"`
	Stale bool `json:"stale"`
}

type gobpgclient struct {
	cmdPath  string
	hostAddr string
}

func (gc *gobpgclient) globalRib(t *testing.T) (map[string][]ribEntry, error) {
	out, err := exec.Command(gc.cmdPath, "-u", gc.hostAddr, "-j", "global", "rib").Output()
	if err != nil {
		return nil, err
	}
	var rv map[string][]ribEntry
	err = json.Unmarshal(out, &rv)
	if err != nil {
		t.Logf("raw: %s\n", out)
		return nil, err
	}
	return rv, nil
}

type gobgpserver struct {
	cmdPath string
	cmd     *exec.Cmd
}

func (gs *gobgpserver) start() error {
	gs.cmd = exec.Command(gs.cmdPath,
		"-p",
		"-f", filepath.Join("test_data", "bgp.toml"),
		"--api-hosts", "127.0.0.3:50051",
		"-l", "info")
	gs.cmd.Stdout = os.Stdout
	gs.cmd.Stderr = os.Stderr
	return gs.cmd.Start()
}

func (gs *gobgpserver) stop() error {
	if gs.cmd.Process != nil {
		return gs.cmd.Process.Kill()
	}
	return nil
}


================================================
FILE: bgp/bgp_windows.go
================================================
package bgp

import (
	"errors"
	"github.com/fabiolb/fabio/config"
)

type BGPHandler struct{}

var ErrNoWindows = errors.New("cannot run bgp on windows")

func NewBGPHandler(config *config.BGP) (*BGPHandler, error) {
	return nil, ErrNoWindows
}

func (bgph *BGPHandler) Start() error {
	return ErrNoWindows
}

func ValidateConfig(config *config.BGP) error {
	return ErrNoWindows
}


================================================
FILE: bgp/logger.go
================================================
package bgp

import (
	"fmt"
	"log"
	"strings"

	"github.com/fabiolb/fabio/exit"
	"github.com/fabiolb/fabio/logger"

	bgplog "github.com/osrg/gobgp/v3/pkg/log"
)

type bgpLogger struct{}

func (l bgpLogger) Panic(msg string, fields bgplog.Fields) {
	exit.Fatal(convertMsgFields("FATAL", msg, fields))
}

func (l bgpLogger) Fatal(msg string, fields bgplog.Fields) {
	exit.Fatal(convertMsgFields("FATAL", msg, fields))
}

func (l bgpLogger) Error(msg string, fields bgplog.Fields) {
	log.Printf("%s", convertMsgFields("ERROR", msg, fields))
}

func (l bgpLogger) Warn(msg string, fields bgplog.Fields) {
	log.Printf("%s", convertMsgFields("WARN", msg, fields))
}

func (l bgpLogger) Info(msg string, fields bgplog.Fields) {
	log.Printf("%s", convertMsgFields("INFO", msg, fields))
}

func (l bgpLogger) Debug(msg string, fields bgplog.Fields) {
	log.Printf("%s", convertMsgFields("DEBUG", msg, fields))
}

func (l bgpLogger) SetLevel(level bgplog.LogLevel) {
	// noop
}

func (l bgpLogger) GetLevel() bgplog.LogLevel {
	lw, ok := log.Writer().(*logger.LevelWriter)
	if !ok {
		return bgplog.InfoLevel
	}

	switch lw.Level() {
	case "TRACE":
		return bgplog.TraceLevel
	case "DEBUG":
		return bgplog.DebugLevel
	case "INFO":
		return bgplog.InfoLevel
	case "WARN":
		return bgplog.WarnLevel
	case "ERROR":
		return bgplog.ErrorLevel
	case "FATAL":
		return bgplog.FatalLevel
	default:
		return bgplog.InfoLevel
	}
}

func convertMsgFields(level, msg string, fields bgplog.Fields) string {
	var b strings.Builder
	fmt.Fprintf(&b, "[%s] gobgpd %s", level, msg)
	for k, v := range fields {
		fmt.Fprintf(&b, " %s=>%v", k, v)
	}
	return b.String()
}


================================================
FILE: bgp/test_data/bgp.toml
================================================
[[neighbors]]
  [neighbors.config]
    neighbor-address = "127.0.0.2"
    peer-as = 65000
  [neighbors.transport.config]
    remote-port = 1790
    passive-mode = false
    local-address = "127.0.0.3"

[global.config]
  as = 65001
  router-id = "127.0.0.3"
  port = 1790
  #port = 179
  local-address-list = [ "127.0.0.3" ]


================================================
FILE: build/ca-certificates.crt
================================================
-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE
AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x
CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW
MBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZF
RElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHkWLn7
09gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7
XBZXehuDYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5P
Grjm6gSSrj0RuVFCPYewMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAK
t0SdE3QrwqXrIhWYENiLxQSfHY9g5QYbm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+Yb
X79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbkHQl/Sog4P75n/TSW9R28
MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTTxKJxqvQU
fecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI
2Sf23EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyH
K9caUPgn6C9D4zq92Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEae
ZAwUswdbxcJzbPEHXEUkFDWug/FqTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAP
BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz4SsrSbbXc6GqlPUB53NlTKxQ
MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU9QHnc2VMrFAw
RAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWIm
fQwng4/F9tqgaHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3
gvoFNTPhNahXwOf9jU8/kzJPeGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKe
I6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1PwkzQSulgUV1qzOMPPKC8W64iLgpq0i
5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1ThCojz2GuHURwCRi
ipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oIKiMn
MCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZ
o5NjEFIqnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6
zqylfDJKZ0DcMDQj3dcEI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacN
GHk0vFQYXlPKNFHtRQrmjseCNj6nOGOpMCwXEGCSn1WHElkQwg9naRHMTh5+Spqt
r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK
Z05phkOTOPu220+DkdRgfks+KzgHVZhepA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsx
CzAJBgNVBAYTAkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRp
ZmljYWNpw7NuIERpZ2l0YWwgLSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwa
QUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4wHhcNMDYxMTI3MjA0NjI5WhcNMzAw
NDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+U29jaWVkYWQgQ2Ft
ZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJhIFMu
QS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeG
qentLhM0R7LQcNzJPNCNyu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzL
fDe3fezTf3MZsGqy2IiKLUV0qPezuMDU2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQ
Y5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU34ojC2I+GdV75LaeHM/J4
Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP2yYe68yQ
54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+b
MMCm8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48j
ilSH5L887uvDdUhfHjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++Ej
YfDIJss2yKHzMI+ko6Kh3VOz3vCaMh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/zt
A/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK5lw1omdMEWux+IBkAC1vImHF
rEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1bczwmPS9KvqfJ
pxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCB
lTCBkgYEVR0gADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFy
YS5jb20vZHBjLzBaBggrBgEFBQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW50
7WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2UgcHVlZGVuIGVuY29udHJhciBlbiBs
YSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEfAygPU3zmpFmps4p6
xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuXEpBc
unvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/
Jre7Ir5v/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dp
ezy4ydV/NgIlqmjCMRW3MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42
gzmRkBDI8ck1fj+404HGIGQatlDCIaR43NAvO2STdPCWkPHv+wlaNECW8DYSwaN0
jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wkeZBWN7PGKX6jD/EpOe9+
XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f/RWmnkJD
W2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/
RL5hRqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35r
MDOhYil/SrnhLecUIw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxk
BYn8eNZcLCZDqQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw
MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD
VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul
CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n
tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl
dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch
PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC
+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O
BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB
IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X
7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz
43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl
pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA
WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx
MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB
ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV
BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV
6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX
GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP
dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH
1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF
62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW
BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL
MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv
b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6
IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/
iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh
4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm
XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1
MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK
EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh
BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq
xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G
87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i
2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U
WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1
0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G
A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr
pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm
aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv
hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm
hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3
P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y
iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no
xqE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP
Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr
ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL
MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1
yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr
VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/
nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG
XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj
vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt
Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g
N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC
nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y
YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua
kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL
QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp
6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG
yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i
QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO
tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu
QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ
Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u
olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48
x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz
dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG
A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U
cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf
qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ
JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ
+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS
s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5
HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7
70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG
V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S
qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S
5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia
C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX
OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE
FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2
KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B
8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ
MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc
0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ
u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF
u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH
YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8
GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO
RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e
KeC2uAloGRwYQw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC
VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ
cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ
BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt
VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D
0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9
ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G
A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs
aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I
flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2
MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk
hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym
1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW
OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb
2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko
O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU
AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF
Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb
LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir
oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C
MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2
MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC
206B89enfHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFci
KtZHgVdEglZTvYYUAQv8f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2
JxhP7JsowtS013wMPgwr38oE18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9
BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7e
Xz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8B
PeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67
Xnfn6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEq
Z8A9W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ
o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3
+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124HhnAgMBAAGj
YzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3OpaaEg5+31IqEj
FNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE
AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmn
xPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2
LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzccc
obGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXgJXUjhx5c3LqdsKyzadsXg8n33gy8
CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMe
IjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMA
DjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2F
AjgQ5ANh1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUX
Om/9riW99XJZZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPb
AZO1XB4Y3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQl
Zvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw
RY8mkaKO/qk=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc
MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp
b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT
AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs
aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H
j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K
f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55
IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw
FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht
QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm
/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ
k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ
MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC
seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ
hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+
eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U
DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj
B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
rosot4LKGAfmt1t06SAZf7IbiVQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJB
VDFIMEYGA1UECgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBp
bSBlbGVrdHIuIERhdGVudmVya2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5R
dWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5RdWFsLTAzMB4XDTA1MDgxNzIyMDAw
MFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgwRgYDVQQKDD9BLVRy
dXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0ZW52
ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMM
EEEtVHJ1c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCtPWFuA/OQO8BBC4SAzewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUj
lUC5B3ilJfYKvUWG6Nm9wASOhURh73+nyfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZ
znF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPESU7l0+m0iKsMrmKS1GWH
2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4iHQF63n1
k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs
2e3Vcuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYD
VR0OBAoECERqlWdVeRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
AQEAVdRU0VlIXLOThaq/Yy/kgM40ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fG
KOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmrsQd7TZjTXLDR8KdCoLXEjq/+
8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZdJXDRZslo+S4R
FGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS
mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmE
DNuxUCAKGkq6ahq97BvIxYSazQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE
BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy
MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD
VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD
VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv
ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl
AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF
661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9
am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1
ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481
PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS
3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k
SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF
3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM
ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g
StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz
Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB
jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg
Q2xhc3MgMiBDQSAxMB4XDTA2MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzEL
MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD
VQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7McXA0
ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLX
l18xoS830r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVB
HfCuuCkslFJgNJQ72uA40Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B
5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/RuFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3
WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0PAQH/BAQD
AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLP
gcIV1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+
DKhQ7SLHrQVMdvvt7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKu
BctN518fV4bVIJwo+28TOPX2EZL2fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHs
h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q
Download .txt
gitextract_kam7pb3_/

├── .dockerignore
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE.md
│   └── workflows/
│       ├── build.yml
│       ├── github-pages.yml
│       └── go-releaser.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── Dockerfile-goreleaser
├── LICENSE
├── Makefile
├── NOTICES.txt
├── README.md
├── admin/
│   ├── api/
│   │   ├── api.go
│   │   ├── config.go
│   │   ├── manual.go
│   │   ├── paths.go
│   │   ├── routes.go
│   │   └── version.go
│   ├── server.go
│   ├── server_test.go
│   └── ui/
│       ├── assets/
│       │   └── fonts/
│       │       └── material-icons.css
│       ├── generate.go
│       ├── manual.go
│       ├── route.go
│       └── static.go
├── assert/
│   └── assert.go
├── auth/
│   ├── auth.go
│   ├── auth_test.go
│   ├── basic.go
│   └── basic_test.go
├── bgp/
│   ├── bgp_nonwindows.go
│   ├── bgp_nonwindows_test.go
│   ├── bgp_windows.go
│   ├── logger.go
│   └── test_data/
│       └── bgp.toml
├── build/
│   ├── ca-certificates.crt
│   ├── homebrew.sh
│   ├── homebrew.vim
│   ├── issue-225-gen-cert.bash
│   ├── releasenotes.pl
│   ├── tag.sh
│   └── update-ssl.sh
├── cert/
│   ├── consul_source.go
│   ├── consul_source_test.go
│   ├── file_source.go
│   ├── http_source.go
│   ├── load.go
│   ├── load_test.go
│   ├── path_source.go
│   ├── source.go
│   ├── source_test.go
│   ├── store.go
│   ├── store_test.go
│   ├── vault_client.go
│   ├── vault_pki_source.go
│   ├── vault_source.go
│   └── watch.go
├── config/
│   ├── config.go
│   ├── default.go
│   ├── flagset.go
│   ├── flagset_test.go
│   ├── kvslice.go
│   ├── kvslice_test.go
│   ├── load.go
│   ├── load_test.go
│   └── localip.go
├── demo/
│   └── aws/
│       ├── .gitignore
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
├── docs/
│   ├── .gitignore
│   ├── README.md
│   ├── archetypes/
│   │   └── default.md
│   ├── config.toml
│   ├── content/
│   │   ├── _index.md
│   │   ├── cfg/
│   │   │   └── _index.md
│   │   ├── code-of-conduct/
│   │   │   └── _index.md
│   │   ├── contact/
│   │   │   └── _index.md
│   │   ├── contrib/
│   │   │   ├── _index.md
│   │   │   ├── development.md
│   │   │   └── guidelines.md
│   │   ├── deploy/
│   │   │   ├── _index.md
│   │   │   ├── amazon-api-gw.md
│   │   │   ├── amazon-elb.md
│   │   │   ├── direct.md
│   │   │   └── existing-lb.md
│   │   ├── faq/
│   │   │   ├── _index.md
│   │   │   ├── binding-to-low-ports.md
│   │   │   ├── multiple-protocol-listeners.md
│   │   │   ├── request-debugging.md
│   │   │   ├── verifying-releases.md
│   │   │   └── why-fabio.md
│   │   ├── feature/
│   │   │   ├── _index.md
│   │   │   ├── access-control.md
│   │   │   ├── access-logging.md
│   │   │   ├── authorization.md
│   │   │   ├── bgp.md
│   │   │   ├── certificate-stores.md
│   │   │   ├── docker.md
│   │   │   ├── dynamic-reloading.md
│   │   │   ├── graceful-shutdown.md
│   │   │   ├── grpc-proxy.md
│   │   │   ├── http-compression.md
│   │   │   ├── http-headers.md
│   │   │   ├── http-path-prepending.md
│   │   │   ├── http-path-stripping.md
│   │   │   ├── http-redirects.md
│   │   │   ├── https-tcp-sni-proxy.md
│   │   │   ├── https-upstream.md
│   │   │   ├── metrics.md
│   │   │   ├── proxy-protocol.md
│   │   │   ├── sse.md
│   │   │   ├── tcp-dynamic-proxy.md
│   │   │   ├── tcp-proxy.md
│   │   │   ├── tcp-sni-proxy.md
│   │   │   ├── traffic-shaping.md
│   │   │   ├── vault.md
│   │   │   ├── web-ui.md
│   │   │   └── websockets.md
│   │   ├── quickstart/
│   │   │   └── _index.md
│   │   └── ref/
│   │       ├── _index.md
│   │       ├── bgp.anycastaddresses.md
│   │       ├── bgp.asn.md
│   │       ├── bgp.certfile.md
│   │       ├── bgp.enabled.md
│   │       ├── bgp.enablegrpc.md
│   │       ├── bgp.gobgpdcfgfile.md
│   │       ├── bgp.grpclistenaddress.md
│   │       ├── bgp.grpctls.md
│   │       ├── bgp.keyfile.md
│   │       ├── bgp.listenaddresses.md
│   │       ├── bgp.listenport.md
│   │       ├── bgp.nexthop.md
│   │       ├── bgp.peers.md
│   │       ├── bgp.routerid.md
│   │       ├── glob.cache.size.md
│   │       ├── glob.matching.disabled.md
│   │       ├── log.access.format.md
│   │       ├── log.access.target.md
│   │       ├── log.level.md
│   │       ├── log.routes.format.md
│   │       ├── metrics.circonus.apiapp.md
│   │       ├── metrics.circonus.apikey.md
│   │       ├── metrics.circonus.apiurl.md
│   │       ├── metrics.circonus.brokerid.md
│   │       ├── metrics.circonus.checkid.md
│   │       ├── metrics.circonus.submissionurl.md
│   │       ├── metrics.dogstatsd.addr.md
│   │       ├── metrics.graphite.addr.md
│   │       ├── metrics.interval.md
│   │       ├── metrics.names.md
│   │       ├── metrics.prefix.md
│   │       ├── metrics.prometheus.buckets.md
│   │       ├── metrics.prometheus.path.md
│   │       ├── metrics.prometheus.subsystem.md
│   │       ├── metrics.retry.md
│   │       ├── metrics.statsd.addr.md
│   │       ├── metrics.target.md
│   │       ├── metrics.timeout.md
│   │       ├── proxy.addr.md
│   │       ├── proxy.auth.md
│   │       ├── proxy.cs.md
│   │       ├── proxy.deregistergraceperiod.md
│   │       ├── proxy.dialtimeout.md
│   │       ├── proxy.flushinterval.md
│   │       ├── proxy.globalflushinterval.md
│   │       ├── proxy.grpcmaxrxmsgsize.md
│   │       ├── proxy.grpcmaxtxmsgsize.md
│   │       ├── proxy.grpcshutdowntimeout.md
│   │       ├── proxy.gzip.contenttype.md
│   │       ├── proxy.header.clientip.md
│   │       ├── proxy.header.requestid.md
│   │       ├── proxy.header.sts.maxage.md
│   │       ├── proxy.header.sts.preload.md
│   │       ├── proxy.header.sts.subdomains.md
│   │       ├── proxy.header.tls.md
│   │       ├── proxy.header.tls.value.md
│   │       ├── proxy.idleconntimeout.md
│   │       ├── proxy.keepalivetimeout.md
│   │       ├── proxy.localip.md
│   │       ├── proxy.matcher.md
│   │       ├── proxy.maxconn.md
│   │       ├── proxy.noroutestatus.md
│   │       ├── proxy.responseheadertimeout.md
│   │       ├── proxy.shutdownwait.md
│   │       ├── proxy.strategy.md
│   │       ├── registry.backend.md
│   │       ├── registry.consul.addr.md
│   │       ├── registry.consul.checksRequired.md
│   │       ├── registry.consul.kvpath.md
│   │       ├── registry.consul.namespace.md
│   │       ├── registry.consul.noroutehtmlpath.md
│   │       ├── registry.consul.pollInterval.md
│   │       ├── registry.consul.register.addr.md
│   │       ├── registry.consul.register.checkInterval.md
│   │       ├── registry.consul.register.checkTLSSkipVerify.md
│   │       ├── registry.consul.register.checkTimeout.md
│   │       ├── registry.consul.register.deregisterCriticalServiceAfter.md
│   │       ├── registry.consul.register.enabled.md
│   │       ├── registry.consul.register.name.md
│   │       ├── registry.consul.register.tags.md
│   │       ├── registry.consul.service.status.md
│   │       ├── registry.consul.serviceMonitors.md
│   │       ├── registry.consul.tagprefix.md
│   │       ├── registry.consul.token.md
│   │       ├── registry.custom.checkTLSSkipVerify.md
│   │       ├── registry.custom.host.md
│   │       ├── registry.custom.path.md
│   │       ├── registry.custom.pollinginterval.md
│   │       ├── registry.custom.queryparams.md
│   │       ├── registry.custom.scheme.md
│   │       ├── registry.custom.timeout.md
│   │       ├── registry.file.noroutehtmlpath.md
│   │       ├── registry.file.path.md
│   │       ├── registry.retry.md
│   │       ├── registry.static.noroutehtmlpath.md
│   │       ├── registry.static.routes.md
│   │       ├── registry.timeout.md
│   │       ├── runtime.gogc.md
│   │       ├── runtime.gomaxprocs.md
│   │       ├── ui.access.md
│   │       ├── ui.addr.md
│   │       ├── ui.color.md
│   │       ├── ui.routingtable.source.host.md
│   │       ├── ui.routingtable.source.linkenabled.md
│   │       ├── ui.routingtable.source.newtab.md
│   │       ├── ui.routingtable.source.port.md
│   │       ├── ui.routingtable.source.scheme.md
│   │       └── ui.title.md
│   ├── layouts/
│   │   ├── 404.html
│   │   ├── _default/
│   │   │   ├── section.html
│   │   │   └── single.html
│   │   ├── index.html
│   │   └── partials/
│   │       ├── footer.html
│   │       ├── header.html
│   │       └── sidebar.html
│   └── static/
│       ├── CNAME
│       ├── check/
│       │   └── ok
│       ├── css/
│       │   └── custom.css
│       ├── fonts/
│       │   └── FontAwesome.otf
│       ├── img/
│       │   └── manifest.json
│       └── js/
│           └── custom.js
├── exit/
│   ├── listen.go
│   └── listen_test.go
├── fabio.iml
├── fabio.properties
├── go.mod
├── go.sum
├── logger/
│   ├── level_writer.go
│   ├── level_writer_test.go
│   ├── logger.go
│   ├── logger_test.go
│   └── pattern.go
├── main.go
├── metrics/
│   ├── metrics.go
│   ├── names.go
│   ├── names_test.go
│   ├── provider_circonus.go
│   ├── provider_circonus_test.go
│   ├── provider_discard.go
│   ├── provider_dogstatsd.go
│   ├── provider_dogstatsd_test.go
│   ├── provider_flat.go
│   ├── provider_graphite.go
│   ├── provider_label.go
│   ├── provider_multi.go
│   ├── provider_prometheus.go
│   ├── provider_prometheus_test.go
│   ├── provider_statsd.go
│   └── provider_statsd_test.go
├── noroute/
│   ├── store.go
│   └── store_test.go
├── proxy/
│   ├── grpc_handler.go
│   ├── gzip/
│   │   ├── content_type_test.go
│   │   ├── gzip_handler.go
│   │   └── gzip_handler_test.go
│   ├── http_handler.go
│   ├── http_headers.go
│   ├── http_headers_test.go
│   ├── http_integration_test.go
│   ├── http_proxy.go
│   ├── inetaf_tcpproxy.go
│   ├── inetaf_tcpproxy_integration_test.go
│   ├── internal/
│   │   └── testcert.go
│   ├── listen.go
│   ├── listen_test.go
│   ├── serve.go
│   ├── tcp/
│   │   ├── copy_buffer.go
│   │   ├── proxy_proto.go
│   │   ├── server.go
│   │   ├── sni_proxy.go
│   │   ├── tcp_dynamic_proxy.go
│   │   ├── tcp_proxy.go
│   │   ├── tcptest/
│   │   │   ├── dialer.go
│   │   │   └── server.go
│   │   ├── tls_clienthello.go
│   │   └── tls_clienthello_test.go
│   ├── tcp_integration_test.go
│   ├── ws_handler.go
│   └── ws_integration_test.go
├── registry/
│   ├── backend.go
│   ├── consul/
│   │   ├── backend.go
│   │   ├── kv.go
│   │   ├── passing.go
│   │   ├── passing_test.go
│   │   ├── register.go
│   │   ├── routecmd.go
│   │   ├── routecmd_test.go
│   │   └── service.go
│   ├── custom/
│   │   ├── backend.go
│   │   ├── custom.go
│   │   └── custom_test.go
│   ├── file/
│   │   └── backend.go
│   └── static/
│       └── backend.go
├── rootwarn_unix.go
├── rootwarn_windows.go
├── route/
│   ├── access_rules.go
│   ├── access_rules_test.go
│   ├── auth.go
│   ├── auth_test.go
│   ├── glob_cache.go
│   ├── glob_cache_test.go
│   ├── issue57_test.go
│   ├── matcher.go
│   ├── matcher_test.go
│   ├── metrics_cleanup_test.go
│   ├── parse_new.go
│   ├── parse_test.go
│   ├── picker.go
│   ├── picker_test.go
│   ├── route.go
│   ├── route_bench_test.go
│   ├── route_def.go
│   ├── routes.go
│   ├── table.go
│   ├── table_registry_test.go
│   ├── table_test.go
│   ├── target.go
│   └── target_test.go
├── transport/
│   └── transport.go
└── uuid/
    ├── format.go
    └── uuid.go
Download .txt
SYMBOL INDEX (892 symbols across 136 files)

FILE: admin/api/api.go
  function writeJSON (line 10) | func writeJSON(w http.ResponseWriter, r *http.Request, v interface{}) {

FILE: admin/api/config.go
  type ConfigHandler (line 5) | type ConfigHandler struct
    method ServeHTTP (line 9) | func (h *ConfigHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque...

FILE: admin/api/manual.go
  type ManualHandler (line 12) | type ManualHandler struct
    method ServeHTTP (line 21) | func (h *ManualHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque...
  type manual (line 16) | type manual struct

FILE: admin/api/paths.go
  type ManualPathsHandler (line 11) | type ManualPathsHandler struct
    method ServeHTTP (line 15) | func (h *ManualPathsHandler) ServeHTTP(w http.ResponseWriter, r *http....

FILE: admin/api/routes.go
  type RoutesHandler (line 12) | type RoutesHandler struct
    method ServeHTTP (line 28) | func (h *RoutesHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque...
  type apiRoute (line 14) | type apiRoute struct

FILE: admin/api/version.go
  type VersionHandler (line 8) | type VersionHandler struct
    method ServeHTTP (line 12) | func (h *VersionHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ...

FILE: admin/server.go
  type Server (line 16) | type Server struct
    method ListenAndServe (line 26) | func (s *Server) ListenAndServe(l config.Listen, tlscfg *tls.Config) e...
    method handler (line 30) | func (s *Server) handler() http.Handler {
  function handleHealth (line 76) | func handleHealth(w http.ResponseWriter, r *http.Request) {
  function forbidden (line 80) | func forbidden(w http.ResponseWriter, r *http.Request) {

FILE: admin/server_test.go
  function TestAdminServerAccess (line 11) | func TestAdminServerAccess(t *testing.T) {

FILE: admin/ui/manual.go
  type ManualHandler (line 8) | type ManualHandler struct
    method ServeHTTP (line 16) | func (h *ManualHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque...

FILE: admin/ui/route.go
  type RoutesHandler (line 11) | type RoutesHandler struct
    method ServeHTTP (line 18) | func (h *RoutesHandler) ServeHTTP(w http.ResponseWriter, _ *http.Reque...

FILE: assert/assert.go
  function Equal (line 13) | func Equal(t *testing.T) func(got, want interface{}) {
  function EqualDepth (line 17) | func EqualDepth(t *testing.T, calldepth int, desc string) func(got, want...

FILE: auth/auth.go
  type AuthScheme (line 10) | type AuthScheme interface
  function LoadAuthSchemes (line 14) | func LoadAuthSchemes(cfg map[string]config.AuthScheme) (map[string]AuthS...

FILE: auth/auth_test.go
  function TestLoadAuthSchemes (line 9) | func TestLoadAuthSchemes(t *testing.T) {

FILE: auth/basic.go
  type basic (line 15) | type basic struct
    method Authorized (line 77) | func (b *basic) Authorized(request *http.Request, response http.Respon...
  function newBasicAuth (line 20) | func newBasicAuth(cfg config.BasicAuth) (AuthScheme, error) {

FILE: auth/basic_test.go
  type responseWriter (line 17) | type responseWriter struct
    method Header (line 23) | func (rw *responseWriter) Header() http.Header {
    method Write (line 30) | func (rw *responseWriter) Write(b []byte) (int, error) {
    method WriteHeader (line 35) | func (rw *responseWriter) WriteHeader(statusCode int) {
  function createBasicAuthFile (line 39) | func createBasicAuthFile(contents string, t *testing.T) (string, error) {
  function createBasicAuth (line 53) | func createBasicAuth(user string, password string, t *testing.T) (AuthSc...
  function TestNewBasicAuth (line 73) | func TestNewBasicAuth(t *testing.T) {
  function TestBasic_Authorised (line 108) | func TestBasic_Authorised(t *testing.T) {
  function TestBasic_Authorised_should_fail_without_htpasswd_file (line 171) | func TestBasic_Authorised_should_fail_without_htpasswd_file(t *testing.T) {
  function TestBasic_Authorized_should_set_www_realm_header (line 213) | func TestBasic_Authorized_should_set_www_realm_header(t *testing.T) {

FILE: bgp/bgp_nonwindows.go
  constant denyAllNeighbors (line 37) | denyAllNeighbors = "deny-all-neighbors"
  constant matchAnyPeer (line 38) | matchAnyPeer     = "match-any-peer"
  constant globalTable (line 39) | globalTable      = "global"
  constant rejectAll (line 40) | rejectAll        = "reject-all"
  type BGPHandler (line 43) | type BGPHandler struct
    method Start (line 106) | func (bgph *BGPHandler) Start() error {
    method startBGP (line 168) | func (bgph *BGPHandler) startBGP(ctx context.Context) error {
    method setPolicies (line 179) | func (bgph *BGPHandler) setPolicies() error {
    method addNeighbors (line 229) | func (bgph *BGPHandler) addNeighbors(ctx context.Context, peers []conf...
    method AddRoutes (line 275) | func (bgph *BGPHandler) AddRoutes(ctx context.Context, routes []string...
    method DeleteRoutes (line 320) | func (bgph *BGPHandler) DeleteRoutes(ctx context.Context, routes []str...
  function NewBGPHandler (line 49) | func NewBGPHandler(config *config.BGP) (*BGPHandler, error) {
  function ValidateConfig (line 361) | func ValidateConfig(config *config.BGP) error {

FILE: bgp/bgp_nonwindows_test.go
  function TestBGPHandler (line 18) | func TestBGPHandler(t *testing.T) {
  type ribEntry (line 130) | type ribEntry struct
  type gobpgclient (line 149) | type gobpgclient struct
    method globalRib (line 154) | func (gc *gobpgclient) globalRib(t *testing.T) (map[string][]ribEntry,...
  type gobgpserver (line 168) | type gobgpserver struct
    method start (line 173) | func (gs *gobgpserver) start() error {
    method stop (line 184) | func (gs *gobgpserver) stop() error {

FILE: bgp/bgp_windows.go
  type BGPHandler (line 8) | type BGPHandler struct
    method Start (line 16) | func (bgph *BGPHandler) Start() error {
  function NewBGPHandler (line 12) | func NewBGPHandler(config *config.BGP) (*BGPHandler, error) {
  function ValidateConfig (line 20) | func ValidateConfig(config *config.BGP) error {

FILE: bgp/logger.go
  type bgpLogger (line 14) | type bgpLogger struct
    method Panic (line 16) | func (l bgpLogger) Panic(msg string, fields bgplog.Fields) {
    method Fatal (line 20) | func (l bgpLogger) Fatal(msg string, fields bgplog.Fields) {
    method Error (line 24) | func (l bgpLogger) Error(msg string, fields bgplog.Fields) {
    method Warn (line 28) | func (l bgpLogger) Warn(msg string, fields bgplog.Fields) {
    method Info (line 32) | func (l bgpLogger) Info(msg string, fields bgplog.Fields) {
    method Debug (line 36) | func (l bgpLogger) Debug(msg string, fields bgplog.Fields) {
    method SetLevel (line 40) | func (l bgpLogger) SetLevel(level bgplog.LogLevel) {
    method GetLevel (line 44) | func (l bgpLogger) GetLevel() bgplog.LogLevel {
  function convertMsgFields (line 68) | func convertMsgFields(level, msg string, fields bgplog.Fields) string {

FILE: cert/consul_source.go
  type ConsulSource (line 23) | type ConsulSource struct
    method LoadClientCAs (line 55) | func (s ConsulSource) LoadClientCAs() (*x509.CertPool, error) {
    method Certificates (line 77) | func (s ConsulSource) Certificates() chan []tls.Certificate {
  function parseConsulURL (line 29) | func parseConsulURL(rawurl string) (config *api.Config, key string, err ...
  function watchKV (line 110) | func watchKV(client *api.Client, key string, pemBlocks chan map[string][...
  function getCerts (line 130) | func getCerts(client *api.Client, key string, waitIndex uint64) (pemBloc...

FILE: cert/consul_source_test.go
  function TestParseConsulURL (line 10) | func TestParseConsulURL(t *testing.T) {

FILE: cert/file_source.go
  type FileSource (line 17) | type FileSource struct
    method LoadClientCAs (line 24) | func (s FileSource) LoadClientCAs() (*x509.CertPool, error) {
    method Certificates (line 34) | func (s FileSource) Certificates() chan []tls.Certificate {
  function loadX509KeyPair (line 41) | func loadX509KeyPair(certFile, keyFile string) tls.Certificate {

FILE: cert/http_source.go
  type HTTPSource (line 17) | type HTTPSource struct
    method LoadClientCAs (line 24) | func (s HTTPSource) LoadClientCAs() (*x509.CertPool, error) {
    method Certificates (line 28) | func (s HTTPSource) Certificates() chan []tls.Certificate {

FILE: cert/load.go
  constant MaxSize (line 21) | MaxSize = 1 << 20
  function loadURL (line 23) | func loadURL(listURL string) (pemBlocks map[string][]byte, err error) {
  function loadPath (line 68) | func loadPath(root string) (pemBlocks map[string][]byte, err error) {
  function loadCertificates (line 109) | func loadCertificates(pemBlocks map[string][]byte) ([]tls.Certificate, e...
  function base (line 161) | func base(rawurl string) (string, error) {
  function replaceSuffix (line 177) | func replaceSuffix(s string, oldSuffix, newSuffix string) string {
  function newCertPool (line 183) | func newCertPool(path string, caUpgradeCN string, loadFn func(path strin...
  function upgradeCACertificate (line 211) | func upgradeCACertificate(cert *x509.Certificate, caUpgradeCN string) {

FILE: cert/load_test.go
  function TestBase (line 9) | func TestBase(t *testing.T) {
  function TestReplaceSuffix (line 36) | func TestReplaceSuffix(t *testing.T) {
  function TestUpgradeCACertificate (line 42) | func TestUpgradeCACertificate(t *testing.T) {

FILE: cert/path_source.go
  constant DefaultCertPath (line 11) | DefaultCertPath     = "cert"
  constant DefaultClientCAPath (line 12) | DefaultClientCAPath = "clientca"
  type PathSource (line 15) | type PathSource struct
    method LoadClientCAs (line 23) | func (s PathSource) LoadClientCAs() (*x509.CertPool, error) {
    method Certificates (line 28) | func (s PathSource) Certificates() chan []tls.Certificate {
  function makePath (line 35) | func makePath(parent, child, defaultChild string) string {

FILE: cert/source.go
  type Source (line 13) | type Source interface
  type Issuer (line 27) | type Issuer interface
  function NewSource (line 34) | func NewSource(cfg config.CertSource) (Source, error) {
  function TLSConfig (line 95) | func TLSConfig(src Source, strictMatch bool, minVersion, maxVersion uint...

FILE: cert/source_test.go
  function TestTLSConfig (line 33) | func TestTLSConfig(t *testing.T) {
  function TestNewSource (line 73) | func TestNewSource(t *testing.T) {
  type StaticSource (line 169) | type StaticSource struct
    method Certificates (line 174) | func (s StaticSource) Certificates() chan []tls.Certificate {
    method LoadClientCAs (line 181) | func (s StaticSource) LoadClientCAs() (*x509.CertPool, error) {
  function TestStaticSource (line 185) | func TestStaticSource(t *testing.T) {
  function TestFileSource (line 194) | func TestFileSource(t *testing.T) {
  function TestPathSource (line 202) | func TestPathSource(t *testing.T) {
  function TestHTTPSource (line 210) | func TestHTTPSource(t *testing.T) {
  function TestConsulSource (line 224) | func TestConsulSource(t *testing.T) {
  function vaultServer (line 305) | func vaultServer(t *testing.T, addr, rootToken string) (*exec.Cmd, *vaul...
  function makeToken (line 374) | func makeToken(t *testing.T, c *vaultapi.Client, wrapTTL string, req *va...
  function TestVaultSource (line 433) | func TestVaultSource(t *testing.T) {
  function TestVaultPKISource (line 492) | func TestVaultPKISource(t *testing.T) {
  function testSource (line 562) | func testSource(t *testing.T, source Source, rootCAs *x509.CertPool, sle...
  function roundtrip (line 623) | func roundtrip(serverName string, srvConfig *tls.Config, client *http.Cl...
  function http11Client (line 653) | func http11Client(rootCAs *x509.CertPool) *http.Client {
  function http20Client (line 665) | func http20Client(rootCAs *x509.CertPool) (*http.Client, error) {
  function writeFile (line 677) | func writeFile(filename string, data []byte) {
  function makeCertPool (line 683) | func makeCertPool(x ...[]byte) *x509.CertPool {
  function saveCert (line 694) | func saveCert(dir, host string, certPEM, keyPEM []byte) (certFile, keyFi...
  function makePEM (line 703) | func makePEM(host string, validFor time.Duration) (certPEM, keyPEM []byt...
  function makeCert (line 735) | func makeCert(host string, validFor time.Duration) tls.Certificate {
  function waitFor (line 744) | func waitFor(timeout time.Duration, up func() bool) bool {

FILE: cert/store.go
  type Store (line 14) | type Store struct
    method SetCertificates (line 26) | func (s *Store) SetCertificates(certs []tls.Certificate) {
    method certstore (line 37) | func (s *Store) certstore() certstore {
  function NewStore (line 19) | func NewStore() *Store {
  function getCertificate (line 43) | func getCertificate(cs certstore, clientHello *tls.ClientHelloInfo, stri...
  type certstore (line 81) | type certstore struct
    method BuildNameToCertificate (line 89) | func (c *certstore) BuildNameToCertificate() {

FILE: cert/store_test.go
  function TestGetCertificate (line 11) | func TestGetCertificate(t *testing.T) {

FILE: cert/vault_client.go
  type vaultClient (line 18) | type vaultClient struct
    method Get (line 36) | func (c *vaultClient) Get() (*api.Client, error) {
    method keepTokenAlive (line 123) | func (c *vaultClient) keepTokenAlive() {
  function NewVaultClient (line 28) | func NewVaultClient(fetchVaultToken string) *vaultClient {
  function getVaultToken (line 179) | func getVaultToken(c string) string {

FILE: cert/vault_pki_source.go
  type VaultPKISource (line 21) | type VaultPKISource struct
    method LoadClientCAs (line 45) | func (s *VaultPKISource) LoadClientCAs() (*x509.CertPool, error) {
    method Certificates (line 53) | func (s *VaultPKISource) Certificates() chan []tls.Certificate {
    method Issue (line 57) | func (s *VaultPKISource) Issue(commonName string) (*tls.Certificate, e...
    method formatSerial (line 137) | func (*VaultPKISource) formatSerial(sn *big.Int) string {
  function NewVaultPKISource (line 38) | func NewVaultPKISource() *VaultPKISource {

FILE: cert/vault_source.go
  type VaultSource (line 23) | type VaultSource struct
    method LoadClientCAs (line 31) | func (s *VaultSource) LoadClientCAs() (*x509.CertPool, error) {
    method Certificates (line 38) | func (s *VaultSource) Certificates() chan []tls.Certificate {
    method load (line 44) | func (s *VaultSource) load(path string) (pemBlocks map[string][]byte, ...
    method addPrefixToVKVPath (line 131) | func (s *VaultSource) addPrefixToVKVPath(p, mountPath, apiPrefix strin...
    method isKVv2 (line 136) | func (s *VaultSource) isKVv2(path string, client *api.Client) (string,...
    method kvPreflightVersionRequest (line 145) | func (s *VaultSource) kvPreflightVersionRequest(client *api.Client, pa...

FILE: cert/watch.go
  function watch (line 11) | func watch(ch chan []tls.Certificate, refresh time.Duration, path string...

FILE: config/config.go
  type Config (line 9) | type Config struct
  type CertSource (line 25) | type CertSource struct
  type Listen (line 37) | type Listen struct
  type Source (line 53) | type Source struct
  type RoutingTable (line 61) | type RoutingTable struct
  type UI (line 65) | type UI struct
  type Proxy (line 73) | type Proxy struct
  type STSHeader (line 99) | type STSHeader struct
  type Runtime (line 105) | type Runtime struct
  type Circonus (line 110) | type Circonus struct
  type Prometheus (line 119) | type Prometheus struct
  type Log (line 125) | type Log struct
  type Metrics (line 132) | type Metrics struct
  type Registry (line 146) | type Registry struct
  type Static (line 156) | type Static struct
  type File (line 161) | type File struct
  type Consul (line 166) | type Consul struct
  type Custom (line 191) | type Custom struct
  type AuthScheme (line 202) | type AuthScheme struct
  type BasicAuth (line 208) | type BasicAuth struct
  type ConsulTlS (line 215) | type ConsulTlS struct
  type BGP (line 223) | type BGP struct
  type BGPPeer (line 240) | type BGPPeer struct

FILE: config/flagset.go
  type stringSliceValue (line 13) | type stringSliceValue
    method Set (line 20) | func (v *stringSliceValue) Set(s string) error {
    method Get (line 32) | func (v *stringSliceValue) Get() interface{} { return []string(*v) }
    method String (line 33) | func (v *stringSliceValue) String() string   { return strings.Join(*v,...
  function newStringSliceValue (line 15) | func newStringSliceValue(val []string, p *[]string) *stringSliceValue {
  type floatSliceValue (line 35) | type floatSliceValue
    method String (line 42) | func (f *floatSliceValue) String() string {
    method Set (line 50) | func (f *floatSliceValue) Set(s string) error {
  function newFloatSliceValue (line 37) | func newFloatSliceValue(val []float64, p *[]float64) *floatSliceValue {
  type FlagSet (line 67) | type FlagSet struct
    method IsSet (line 79) | func (f *FlagSet) IsSet(name string) bool {
    method StringSliceVar (line 83) | func (f *FlagSet) StringSliceVar(p *[]string, name string, value []str...
    method FloatSliceVar (line 87) | func (f *FlagSet) FloatSliceVar(p *[]float64, name string, value []flo...
    method ParseFlags (line 95) | func (f *FlagSet) ParseFlags(args, environ, prefixes []string, p *prop...
  function NewFlagSet (line 72) | func NewFlagSet(name string, errorHandling flag.ErrorHandling) *FlagSet {

FILE: config/flagset_test.go
  function TestParseFlags (line 11) | func TestParseFlags(t *testing.T) {
  function TestDefaults (line 90) | func TestDefaults(t *testing.T) {

FILE: config/kvslice.go
  function parseKVSlice (line 20) | func parseKVSlice(in string) ([]map[string]string, error) {
  type itemType (line 126) | type itemType
    method String (line 136) | func (t itemType) String() string {
  constant itemText (line 129) | itemText      itemType = "TEXT"
  constant itemEqual (line 130) | itemEqual     itemType = "EQUAL"
  constant itemSemicolon (line 131) | itemSemicolon itemType = "SEMICOLON"
  constant itemComma (line 132) | itemComma     itemType = "COMMA"
  constant itemError (line 133) | itemError     itemType = "ERROR"
  type state (line 140) | type state
  constant stateStart (line 145) | stateStart    state = "start"
  constant stateText (line 146) | stateText     state = "text"
  constant stateQText (line 147) | stateQText    state = "qtext"
  constant stateQTextEnd (line 148) | stateQTextEnd state = "qtextend"
  constant stateQTextEsc (line 149) | stateQTextEsc state = "qtextesc"
  constant stateFirstKey (line 152) | stateFirstKey      state = "first-key"
  constant stateKey (line 153) | stateKey           state = "key"
  constant stateEqual (line 154) | stateEqual         state = "equal"
  constant stateVal (line 155) | stateVal           state = "val"
  constant stateAfterFirstKey (line 156) | stateAfterFirstKey state = "equal-comma-semicolon"
  function lex (line 159) | func lex(s []rune) (itemType, string, int) {

FILE: config/kvslice_test.go
  function TestParseKVSlice (line 8) | func TestParseKVSlice(t *testing.T) {

FILE: config/load.go
  function loadCiphers (line 22) | func loadCiphers() {
  function Load (line 32) | func Load(args, environ []string) (cfg *Config, err error) {
  function parse (line 61) | func parse(args []string) (cmdline []string, path string, version bool, ...
  function load (line 116) | func load(cmdline, environ, envprefix []string, props *properties.Proper...
  function parseScheme (line 386) | func parseScheme(s string) (scheme, addr string) {
  function parseListeners (line 404) | func parseListeners(cfgs string, cs map[string]CertSource, readTimeout, ...
  function parseListen (line 416) | func parseListen(cfg map[string]string, cs map[string]CertSource, readTi...
  function parseTLSVersion (line 538) | func parseTLSVersion(s string) (uint16, error) {
  function parseTLSCiphers (line 546) | func parseTLSCiphers(s string) ([]uint16, error) {
  function parseUint16 (line 563) | func parseUint16(s string) (uint16, error) {
  function parseCertSources (line 571) | func parseCertSources(cfgs string) (cs map[string]CertSource, err error) {
  function parseCertSource (line 587) | func parseCertSource(cfg map[string]string) (c CertSource, err error) {
  function parseAuthSchemes (line 647) | func parseAuthSchemes(cfgs string) (as map[string]AuthScheme, err error) {
  function parseAuthScheme (line 663) | func parseAuthScheme(cfg map[string]string) (a AuthScheme, err error) {
  function parseBGPPeers (line 716) | func parseBGPPeers(cfgs string) ([]BGPPeer, error) {
  function parseBGPPeer (line 732) | func parseBGPPeer(cfg map[string]string) (BGPPeer, error) {

FILE: config/load_test.go
  function TestLoad (line 19) | func TestLoad(t *testing.T) {

FILE: config/localip.go
  function LocalIP (line 9) | func LocalIP() (net.IP, error) {
  function LocalIPString (line 24) | func LocalIPString() string {

FILE: exit/listen.go
  function Listen (line 22) | func Listen(fn func(os.Signal)) {
  function Exit (line 61) | func Exit(code int) {
  function Fatal (line 71) | func Fatal(v ...interface{}) {
  function Fatalf (line 77) | func Fatalf(format string, v ...interface{}) {
  function Wait (line 83) | func Wait() {

FILE: exit/listen_test.go
  function TestExit (line 11) | func TestExit(t *testing.T) {

FILE: logger/level_writer.go
  type LevelWriter (line 13) | type LevelWriter struct
    method Write (line 32) | func (w *LevelWriter) Write(b []byte) (int, error) {
    method SetLevel (line 52) | func (w *LevelWriter) SetLevel(s string) bool {
    method Level (line 80) | func (w *LevelWriter) Level() string {
  function NewLevelWriter (line 24) | func NewLevelWriter(w io.Writer, level, prefix string) *LevelWriter {

FILE: logger/level_writer_test.go
  function TestLevelWriter (line 10) | func TestLevelWriter(t *testing.T) {

FILE: logger/logger.go
  constant CommonFormat (line 53) | CommonFormat   = `$remote_host - - [$time_common] "$request" $response_s...
  constant CombinedFormat (line 54) | CombinedFormat = `$remote_host - - [$time_common] "$request" $response_s...
  type Event (line 58) | type Event struct
  type Logger (line 91) | type Logger interface
  function New (line 98) | func New(w io.Writer, format string) (Logger, error) {
  type noopLogger (line 112) | type noopLogger struct
    method Log (line 114) | func (l *noopLogger) Log(*Event) {}
  type logger (line 116) | type logger struct
    method Log (line 135) | func (l *logger) Log(e *Event) {
  constant bufSize (line 124) | bufSize = 1024

FILE: logger/logger_test.go
  function TestParse (line 15) | func TestParse(t *testing.T) {
  function TestLog (line 54) | func TestLog(t *testing.T) {
  function TestAtoi (line 145) | func TestAtoi(t *testing.T) {
  function BenchmarkLog (line 177) | func BenchmarkLog(b *testing.B) {
  function mustParse (line 251) | func mustParse(s string) *url.URL {

FILE: logger/pattern.go
  function init (line 11) | func init() {
  type pattern (line 22) | type pattern
    method write (line 24) | func (p pattern) write(b *bytes.Buffer, e *Event) {
  type field (line 35) | type field
  function hostport (line 284) | func hostport(s string) (host, port string) {
  function atoi (line 294) | func atoi(b *bytes.Buffer, i int64, pad int) {
  function parse (line 333) | func parse(format string, fields map[string]field) (p pattern, err error) {
  type itemType (line 372) | type itemType
    method String (line 380) | func (t itemType) String() string {
  constant itemText (line 375) | itemText itemType = iota
  constant itemField (line 376) | itemField
  constant itemHeader (line 377) | itemHeader
  type state (line 392) | type state
  constant stateStart (line 395) | stateStart state = iota
  constant stateText (line 396) | stateText
  constant stateDollar (line 397) | stateDollar
  constant stateField (line 398) | stateField
  constant stateDot (line 399) | stateDot
  constant stateHeader (line 400) | stateHeader
  function lex (line 403) | func lex(s []rune) (typ itemType, n int) {

FILE: main.go
  function main (line 60) | func main() {
  function newGrpcProxy (line 164) | func newGrpcProxy(cfg *config.Config, tlscfg *tls.Config, statsHandler *...
  function newHTTPProxy (line 186) | func newHTTPProxy(cfg *config.Config, statsHandler *proxy.HttpStatsHandl...
  function lookupHostFn (line 244) | func lookupHostFn(cfg *config.Config, notFound gkm.Counter) func(string)...
  function lookupHostMatcher (line 257) | func lookupHostMatcher(cfg *config.Config) func(context.Context, string)...
  function makeTLSConfig (line 278) | func makeTLSConfig(l config.Listen) (*tls.Config, error) {
  function startAdmin (line 293) | func startAdmin(cfg *config.Config) {
  function startServers (line 316) | func startServers(cfg *config.Config, stats metrics.Provider) {
  function startBGP (line 506) | func startBGP(cfg *config.BGP) {
  function initRuntime (line 518) | func initRuntime(cfg *config.Config) {
  function initBackend (line 534) | func initBackend(cfg *config.Config) {
  function watchBackend (line 569) | func watchBackend(cfg *config.Config, first chan bool) {
  function watchNoRouteHTML (line 630) | func watchNoRouteHTML() {
  function logRoutes (line 646) | func logRoutes(t route.Table, last, next, format string) {
  function toJSON (line 685) | func toJSON(v interface{}) string {
  function unique (line 693) | func unique(strSlice []string) []string {
  function difference (line 706) | func difference(a, b []string) []string {
  function tableSchemes (line 720) | func tableSchemes(r route.Routes) []string {

FILE: metrics/metrics.go
  type Provider (line 13) | type Provider interface
  type DeletableCounter (line 26) | type DeletableCounter interface
  type DeletableGauge (line 34) | type DeletableGauge interface
  type DeletableHistogram (line 41) | type DeletableHistogram interface
  function Initialize (line 47) | func Initialize(cfg *config.Metrics) (Provider, error) {

FILE: metrics/names.go
  type Service (line 13) | type Service struct
    method String (line 38) | func (s Service) String() string {
  constant DefaultNames (line 22) | DefaultNames = "{{clean .Service}}.{{clean .Host}}.{{clean .Path}}.{{cle...
  constant DefaultPrefix (line 25) | DefaultPrefix = "{{clean .Hostname}}.{{clean .Exec}}"
  function init (line 30) | func init() {
  constant DotSeparator (line 42) | DotSeparator = "."
  constant PipeSeparator (line 43) | PipeSeparator = "|"
  constant RoutePrefix (line 44) | RoutePrefix = "route"
  function Flatten (line 46) | func Flatten(name string, labels []string, separator string) string {
  function Labels (line 53) | func Labels(labels, values []string, stringsprefix, fieldsep, recsep str...
  function parseNames (line 73) | func parseNames(tmpl string) (*template.Template, error) {
  function parsePrefix (line 92) | func parsePrefix(tmpl string) (string, error) {
  function clean (line 121) | func clean(s string) string {
  function TargetName (line 134) | func TargetName(service, host, path, target string) (string, error) {
  function TargetNameWith (line 161) | func TargetNameWith(name string, values []string) (string, error) {
  function isRouteMetric (line 182) | func isRouteMetric(name string) bool { return strings.HasPrefix(name, Ro...

FILE: metrics/names_test.go
  function TestParsePrefix (line 8) | func TestParsePrefix(t *testing.T) {
  function TestTargetName (line 30) | func TestTargetName(t *testing.T) {

FILE: metrics/provider_circonus.go
  constant serviceName (line 21) | serviceName = "fabio"
  function NewCirconusProvider (line 23) | func NewCirconusProvider(prefix string, circ config.Circonus, interval t...
  type CirconusProvider (line 71) | type CirconusProvider struct
    method metricName (line 76) | func (cp *CirconusProvider) metricName(name string) string {
    method NewCounter (line 80) | func (cp *CirconusProvider) NewCounter(name string, labels ...string) ...
    method NewGauge (line 88) | func (cp *CirconusProvider) NewGauge(name string, labels ...string) gk...
    method NewHistogram (line 96) | func (cp *CirconusProvider) NewHistogram(name string, labels ...string...
  type cgmCounter (line 104) | type cgmCounter struct
    method With (line 110) | func (c *cgmCounter) With(labelValues ...string) gkm.Counter {
    method Add (line 130) | func (c *cgmCounter) Add(delta float64) {
  type cgmGauge (line 134) | type cgmGauge struct
    method With (line 140) | func (g *cgmGauge) With(labelValues ...string) gkm.Gauge {
    method Set (line 160) | func (g *cgmGauge) Set(value float64) {
    method Add (line 164) | func (g *cgmGauge) Add(delta float64) {
  type cgmTimer (line 168) | type cgmTimer struct
    method With (line 174) | func (t *cgmTimer) With(labelValues ...string) gkm.Histogram {
    method Observe (line 194) | func (t *cgmTimer) Observe(value float64) {

FILE: metrics/provider_circonus_test.go
  function TestAll (line 11) | func TestAll(t *testing.T) {

FILE: metrics/provider_discard.go
  type DiscardProvider (line 8) | type DiscardProvider struct
    method NewCounter (line 10) | func (dp DiscardProvider) NewCounter(name string, labels ...string) gk...
    method NewGauge (line 14) | func (dp DiscardProvider) NewGauge(name string, labels ...string) gkm....
    method NewHistogram (line 18) | func (dp DiscardProvider) NewHistogram(name string, labels ...string) ...

FILE: metrics/provider_dogstatsd.go
  type DogstatsdProvider (line 14) | type DogstatsdProvider struct
    method NewCounter (line 18) | func (dp *DogstatsdProvider) NewCounter(name string, labels ...string)...
    method NewGauge (line 22) | func (dp *DogstatsdProvider) NewGauge(name string, labels ...string) g...
    method NewHistogram (line 26) | func (dp *DogstatsdProvider) NewHistogram(name string, labels ...strin...
  function NewDogstatsdProvider (line 30) | func NewDogstatsdProvider(prefix, addr string, interval time.Duration) (...
  type dogstatsdCounter (line 45) | type dogstatsdCounter struct
    method With (line 59) | func (dh *dogstatsdCounter) With(labelValues ...string) gkm.Counter {
  type dogstatsdGauge (line 48) | type dogstatsdGauge struct
    method With (line 63) | func (dh *dogstatsdGauge) With(labelValues ...string) gkm.Gauge {
  type dogstatsdHistogram (line 51) | type dogstatsdHistogram struct
    method Observe (line 55) | func (dh *dogstatsdHistogram) Observe(value float64) {
    method With (line 67) | func (dh *dogstatsdHistogram) With(labelValues ...string) gkm.Histogram {
  function correctReservedTagKeys (line 71) | func correctReservedTagKeys(labelValues []string) []string {
  function correctReservedTagKey (line 83) | func correctReservedTagKey(label string) string {

FILE: metrics/provider_dogstatsd_test.go
  function TestDogstatsdProvider (line 12) | func TestDogstatsdProvider(t *testing.T) {

FILE: metrics/provider_flat.go
  type flatProvider (line 12) | type flatProvider struct
    method NewCounter (line 16) | func (p *flatProvider) NewCounter(name string, labels ...string) gkm.C...
    method NewGauge (line 20) | func (p *flatProvider) NewGauge(name string, labels ...string) gkm.Gau...
    method NewHistogram (line 24) | func (p *flatProvider) NewHistogram(name string, labels ...string) gkm...
  type flatCounter (line 28) | type flatCounter struct
    method With (line 33) | func (c *flatCounter) With(labelValues ...string) gkm.Counter {
    method Add (line 37) | func (c *flatCounter) Add(v float64) {
  type flatGauge (line 42) | type flatGauge struct
    method Set (line 48) | func (g *flatGauge) Set(n float64) {
    method Add (line 53) | func (g *flatGauge) Add(delta float64) {
    method With (line 66) | func (g *flatGauge) With(labelValues ...string) gkm.Gauge {
  type flatHistogram (line 70) | type flatHistogram struct
    method Observe (line 74) | func (h *flatHistogram) Observe(t float64) {
    method With (line 77) | func (h *flatHistogram) With(labels ...string) gkm.Histogram {

FILE: metrics/provider_graphite.go
  type GraphiteProvider (line 13) | type GraphiteProvider struct
    method NewCounter (line 18) | func (g *GraphiteProvider) NewCounter(name string, labels ...string) g...
    method NewGauge (line 29) | func (g *GraphiteProvider) NewGauge(name string, labels ...string) gkm...
    method NewHistogram (line 40) | func (g *GraphiteProvider) NewHistogram(name string, labels ...string)...
  function NewGraphiteProvider (line 53) | func NewGraphiteProvider(prefix, addr string, buckets int, interval time...
  type graphiteCounter (line 69) | type graphiteCounter struct
    method With (line 76) | func (c *graphiteCounter) With(labelValues ...string) gkm.Counter {
  type graphiteGauge (line 96) | type graphiteGauge struct
    method With (line 103) | func (g *graphiteGauge) With(labelValues ...string) gkm.Gauge {
  type graphiteHistogram (line 123) | type graphiteHistogram struct
    method With (line 130) | func (h *graphiteHistogram) With(labelValues ...string) gkm.Histogram {
    method Observe (line 150) | func (h *graphiteHistogram) Observe(value float64) {

FILE: metrics/provider_label.go
  type labelProvider (line 11) | type labelProvider struct
    method NewCounter (line 15) | func (p *labelProvider) NewCounter(name string, labels ...string) gkm....
    method NewGauge (line 19) | func (p *labelProvider) NewGauge(name string, labels ...string) gkm.Ga...
    method NewHistogram (line 23) | func (p *labelProvider) NewHistogram(name string, labels ...string) gk...
  type labelCounter (line 27) | type labelCounter struct
    method With (line 34) | func (c *labelCounter) With(labelValues ...string) gkm.Counter {
    method Inc (line 45) | func (c *labelCounter) Inc() {
    method Add (line 50) | func (c *labelCounter) Add(delta float64) {
  type labelGauge (line 55) | type labelGauge struct
    method With (line 62) | func (g *labelGauge) With(labelValues ...string) gkm.Gauge {
    method Set (line 72) | func (g *labelGauge) Set(n float64) {
    method Add (line 77) | func (g *labelGauge) Add(delta float64) {
  type labelHistogram (line 90) | type labelHistogram struct
    method With (line 96) | func (h *labelHistogram) With(labels ...string) gkm.Histogram {
    method Observe (line 104) | func (h *labelHistogram) Observe(t float64) {

FILE: metrics/provider_multi.go
  type MultiProvider (line 6) | type MultiProvider struct
    method NewCounter (line 16) | func (mp *MultiProvider) NewCounter(name string, labels ...string) gkm...
    method NewGauge (line 26) | func (mp *MultiProvider) NewGauge(name string, labels ...string) gkm.G...
    method NewHistogram (line 34) | func (mp *MultiProvider) NewHistogram(name string, labels ...string) g...
  function NewMultiProvider (line 10) | func NewMultiProvider(p []Provider) *MultiProvider {
  type MultiCounter (line 43) | type MultiCounter struct
    method Add (line 47) | func (mc *MultiCounter) Add(v float64) {
    method With (line 53) | func (mc *MultiCounter) With(labels ...string) gkm.Counter {
    method DeleteLabelValues (line 62) | func (mc *MultiCounter) DeleteLabelValues(labelValues ...string) bool {
  type MultiGauge (line 75) | type MultiGauge struct
    method Set (line 79) | func (m *MultiGauge) Set(n float64) {
    method With (line 85) | func (m *MultiGauge) With(labels ...string) gkm.Gauge {
    method Add (line 93) | func (m *MultiGauge) Add(val float64) {
    method DeleteLabelValues (line 100) | func (m *MultiGauge) DeleteLabelValues(labelValues ...string) bool {
  type MultiHistogram (line 112) | type MultiHistogram struct
    method With (line 116) | func (m *MultiHistogram) With(labelValues ...string) gkm.Histogram {
    method Observe (line 124) | func (m *MultiHistogram) Observe(value float64) {
    method DeleteLabelValues (line 131) | func (m *MultiHistogram) DeleteLabelValues(labelValues ...string) bool {

FILE: metrics/provider_prometheus.go
  type PromProvider (line 8) | type PromProvider struct
    method NewCounter (line 27) | func (p *PromProvider) NewCounter(name string, labels ...string) gkm.C...
    method NewGauge (line 35) | func (p *PromProvider) NewGauge(name string, labels ...string) gkm.Gau...
    method NewHistogram (line 43) | func (p *PromProvider) NewHistogram(name string, labels ...string) gkm...
  function NewPromProvider (line 13) | func NewPromProvider(namespace, subsystem string, buckets []float64) Pro...
  function makeLabels (line 59) | func makeLabels(lvs []string) promclient.Labels {
  type promCounter (line 68) | type promCounter struct
    method Add (line 73) | func (c *promCounter) Add(delta float64) {
    method With (line 77) | func (c *promCounter) With(labelValues ...string) gkm.Counter {
    method DeleteLabelValues (line 85) | func (c *promCounter) DeleteLabelValues(labelValues ...string) bool {
  type promGauge (line 90) | type promGauge struct
    method Set (line 95) | func (g *promGauge) Set(value float64) {
    method Add (line 99) | func (g *promGauge) Add(delta float64) {
    method With (line 103) | func (g *promGauge) With(labelValues ...string) gkm.Gauge {
    method DeleteLabelValues (line 111) | func (g *promGauge) DeleteLabelValues(labelValues ...string) bool {
  type promHistogram (line 116) | type promHistogram struct
    method Observe (line 121) | func (h *promHistogram) Observe(value float64) {
    method With (line 125) | func (h *promHistogram) With(labelValues ...string) gkm.Histogram {
    method DeleteLabelValues (line 133) | func (h *promHistogram) DeleteLabelValues(labelValues ...string) bool {

FILE: metrics/provider_prometheus_test.go
  function TestPromProviderDeleteLabelValues (line 11) | func TestPromProviderDeleteLabelValues(t *testing.T) {
  function TestPromHistogramWithLabelValues (line 88) | func TestPromHistogramWithLabelValues(t *testing.T) {

FILE: metrics/provider_statsd.go
  type StatsdProvider (line 14) | type StatsdProvider struct
    method NewCounter (line 35) | func (p *StatsdProvider) NewCounter(name string, labels ...string) gkm...
    method NewGauge (line 47) | func (p *StatsdProvider) NewGauge(name string, labels ...string) gkm.G...
    method NewHistogram (line 59) | func (p *StatsdProvider) NewHistogram(name string, labels ...string) g...
  function NewStatsdProvider (line 18) | func NewStatsdProvider(prefix, addr string, interval time.Duration) (*St...
  type statsdCounter (line 72) | type statsdCounter struct
    method With (line 79) | func (c *statsdCounter) With(labelValues ...string) gkm.Counter {
  type statsdGauge (line 99) | type statsdGauge struct
    method With (line 106) | func (g *statsdGauge) With(labelValues ...string) gkm.Gauge {
  type statsdHistogram (line 126) | type statsdHistogram struct
    method With (line 133) | func (h *statsdHistogram) With(labelValues ...string) gkm.Histogram {
    method Observe (line 153) | func (h *statsdHistogram) Observe(value float64) {

FILE: metrics/provider_statsd_test.go
  function TestStatsdProvider (line 16) | func TestStatsdProvider(t *testing.T) {
  type statsdEntry (line 108) | type statsdEntry struct
  function parseStatsdMetrics (line 117) | func parseStatsdMetrics(data io.Reader, prefix string) map[string]statsd...

FILE: noroute/store.go
  function init (line 9) | func init() {
  function GetHTML (line 14) | func GetHTML() string {
  function SetHTML (line 19) | func SetHTML(h string) {

FILE: noroute/store_test.go
  function TestStoreSetGet (line 7) | func TestStoreSetGet(t *testing.T) {

FILE: proxy/grpc_handler.go
  type gRPCServer (line 29) | type gRPCServer struct
    method Close (line 33) | func (s *gRPCServer) Close() error {
    method Shutdown (line 38) | func (s *gRPCServer) Shutdown(ctx context.Context) error {
    method Serve (line 43) | func (s *gRPCServer) Serve(lis net.Listener) error {
  function GetGRPCDirector (line 47) | func GetGRPCDirector(tlscfg *tls.Config, cfg *config.Config) func(ctx co...
  type GrpcProxyInterceptor (line 74) | type GrpcProxyInterceptor struct
    method Stream (line 95) | func (g GrpcProxyInterceptor) Stream(srv interface{}, stream grpc.Serv...
    method lookup (line 130) | func (g GrpcProxyInterceptor) lookup(ctx context.Context, fullMethodNa...
    method getDestinationHostFromMetadata (line 175) | func (g GrpcProxyInterceptor) getDestinationHostFromMetadata(md metada...
  type targetKey (line 80) | type targetKey struct
  type proxyStream (line 82) | type proxyStream struct
    method Context (line 87) | func (p proxyStream) Context() context.Context {
  function makeGRPCTargetKey (line 91) | func makeGRPCTargetKey(t *route.Target) string {
  type GrpcStatsHandler (line 184) | type GrpcStatsHandler struct
    method TagConn (line 194) | func (h *GrpcStatsHandler) TagConn(ctx context.Context, info *stats.Co...
    method TagRPC (line 198) | func (h *GrpcStatsHandler) TagRPC(ctx context.Context, info *stats.RPC...
    method HandleRPC (line 202) | func (h *GrpcStatsHandler) HandleRPC(ctx context.Context, rpc stats.RP...
    method HandleConn (line 220) | func (h *GrpcStatsHandler) HandleConn(ctx context.Context, conn stats....
  type connCtxKey (line 191) | type connCtxKey struct
  type rpcCtxKey (line 192) | type rpcCtxKey struct
  type grpcConnectionPool (line 228) | type grpcConnectionPool struct
    method Get (line 250) | func (p *grpcConnectionPool) Get(ctx context.Context, target *route.Ta...
    method newConnection (line 262) | func (p *grpcConnectionPool) newConnection(target *route.Target) (*grp...
    method Set (line 290) | func (p *grpcConnectionPool) Set(target *route.Target, conn *grpc.Clie...
    method cleanup (line 297) | func (p *grpcConnectionPool) cleanup() {
  function newGrpcConnectionPool (line 236) | func newGrpcConnectionPool(tlscfg *tls.Config, cfg *config.Config) *grpc...
  function hasTarget (line 325) | func hasTarget(tKey string, table route.Table) bool {

FILE: proxy/gzip/content_type_test.go
  function TestContentTypes (line 7) | func TestContentTypes(t *testing.T) {

FILE: proxy/gzip/gzip_handler.go
  constant headerVary (line 23) | headerVary            = "Vary"
  constant headerAccept (line 24) | headerAccept          = "Accept"
  constant headerAcceptEncoding (line 25) | headerAcceptEncoding  = "Accept-Encoding"
  constant headerContentEncoding (line 26) | headerContentEncoding = "Content-Encoding"
  constant headerContentType (line 27) | headerContentType     = "Content-Type"
  constant headerContentLength (line 28) | headerContentLength   = "Content-Length"
  constant encodingGzip (line 29) | encodingGzip          = "gzip"
  function NewGzipHandler (line 41) | func NewGzipHandler(h http.Handler, contentTypes *regexp.Regexp) http.Ha...
  type GzipResponseWriter (line 55) | type GzipResponseWriter struct
    method WriteHeader (line 66) | func (grw *GzipResponseWriter) WriteHeader(code int) {
    method Write (line 82) | func (grw *GzipResponseWriter) Write(b []byte) (int, error) {
    method Close (line 93) | func (grw *GzipResponseWriter) Close() {
    method Hijack (line 100) | func (grw *GzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, ...
  function NewGzipResponseWriter (line 62) | func NewGzipResponseWriter(w http.ResponseWriter, contentTypes *regexp.R...
  function isCompressable (line 107) | func isCompressable(header http.Header, contentTypes *regexp.Regexp) bool {
  function acceptsGzip (line 115) | func acceptsGzip(r *http.Request) bool {

FILE: proxy/gzip/gzip_handler_test.go
  function Test_GzipHandler_CompressableType (line 19) | func Test_GzipHandler_CompressableType(t *testing.T) {
  function Test_GzipHandler_NotCompressingTwice (line 48) | func Test_GzipHandler_NotCompressingTwice(t *testing.T) {
  function Test_GzipHandler_CompressableType_NoAccept (line 72) | func Test_GzipHandler_CompressableType_NoAccept(t *testing.T) {
  function Test_GzipHandler_NonCompressableType (line 92) | func Test_GzipHandler_NonCompressableType(t *testing.T) {
  function test_text_handler (line 112) | func test_text_handler() http.Handler {
  function test_binary_handler (line 120) | func test_binary_handler() http.Handler {
  function test_already_compressed_handler (line 127) | func test_already_compressed_handler() http.Handler {

FILE: proxy/http_handler.go
  constant StatusClientClosedRequest (line 15) | StatusClientClosedRequest = 499
  function newHTTPProxy (line 17) | func newHTTPProxy(target *url.URL, tr http.RoundTripper, flush time.Dura...
  function httpProxyErrorHandler (line 39) | func httpProxyErrorHandler(w http.ResponseWriter, r *http.Request, err e...

FILE: proxy/http_headers.go
  function addResponseHeaders (line 15) | func addResponseHeaders(w http.ResponseWriter, r *http.Request, cfg conf...
  function addHeaders (line 45) | func addHeaders(r *http.Request, cfg config.Proxy, stripPath string) err...
  function uint16base16 (line 174) | func uint16base16(n uint16) string {
  function i32toa (line 185) | func i32toa(n int32) string {
  function scheme (line 214) | func scheme(r *http.Request) string {
  function localPort (line 246) | func localPort(r *http.Request) string {

FILE: proxy/http_headers_test.go
  function TestAddHeaders (line 14) | func TestAddHeaders(t *testing.T) {
  function TestAddResponseHeaders (line 404) | func TestAddResponseHeaders(t *testing.T) {
  function TestLocalPort (line 461) | func TestLocalPort(t *testing.T) {
  function TestUint16Base16 (line 483) | func TestUint16Base16(t *testing.T) {
  function BenchmarkUint16Base16 (line 491) | func BenchmarkUint16Base16(b *testing.B) {

FILE: proxy/http_integration_test.go
  constant globEnabled (line 32) | globEnabled  = false
  constant globDisabled (line 33) | globDisabled = true
  constant legitHeader1 (line 40) | legitHeader1 = "Legit-Header1"
  constant legitHeader2 (line 41) | legitHeader2 = "Legit-Header2"
  function TestProxyProducesCorrectXForwardedSomethingHeader (line 44) | func TestProxyProducesCorrectXForwardedSomethingHeader(t *testing.T) {
  function TestProxyRequestIDHeader (line 84) | func TestProxyRequestIDHeader(t *testing.T) {
  function TestProxySTSHeader (line 109) | func TestProxySTSHeader(t *testing.T) {
  function TestProxyChecksHeaderForAccessRules (line 144) | func TestProxyChecksHeaderForAccessRules(t *testing.T) {
  function TestProxyNoRouteHTML (line 173) | func TestProxyNoRouteHTML(t *testing.T) {
  function TestProxyNoRouteStatus (line 188) | func TestProxyNoRouteStatus(t *testing.T) {
  function TestProxyStripsPath (line 202) | func TestProxyStripsPath(t *testing.T) {
  function TestProxyPrependsPath (line 230) | func TestProxyPrependsPath(t *testing.T) {
  function TestProxyHost (line 258) | func TestProxyHost(t *testing.T) {
  function TestHostRedirect (line 318) | func TestHostRedirect(t *testing.T) {
  function TestPathRedirect (line 357) | func TestPathRedirect(t *testing.T) {
  function TestProxyLogOutput (line 400) | func TestProxyLogOutput(t *testing.T) {
  function testProxyLogOutput (line 416) | func testProxyLogOutput(t *testing.T, bodySize int, cfg config.Proxy) {
  function TestProxyHTTPSUpstream (line 523) | func TestProxyHTTPSUpstream(t *testing.T) {
  type sniHandler (line 548) | type sniHandler struct
    method ServeHTTP (line 552) | func (s *sniHandler) ServeHTTP(writer http.ResponseWriter, request *ht...
  function TestProxyHTTPSTransport (line 559) | func TestProxyHTTPSTransport(t *testing.T) {
  function TestProxyHTTPSUpstreamSkipVerify (line 590) | func TestProxyHTTPSUpstreamSkipVerify(t *testing.T) {
  function TestProxyGzipHandler (line 617) | func TestProxyGzipHandler(t *testing.T) {
  function plainHandler (line 705) | func plainHandler(contentType string) http.HandlerFunc {
  function gzipHandler (line 712) | func gzipHandler(contentType string) http.HandlerFunc {
  function tlsInsecureConfig (line 724) | func tlsInsecureConfig() *tls.Config {
  function tlsClientConfig (line 728) | func tlsClientConfig() *tls.Config {
  function tlsServerConfig (line 739) | func tlsServerConfig() *tls.Config {
  function tlsServerConfig2 (line 747) | func tlsServerConfig2() *tls.Config {
  function mustParse (line 755) | func mustParse(rawurl string) *url.URL {
  function mustDo (line 763) | func mustDo(req *http.Request) (*http.Response, []byte) {
  function mustGet (line 776) | func mustGet(urlstr string) (*http.Response, []byte) {
  function compress (line 785) | func compress(b []byte) []byte {
  function BenchmarkProxyLogger (line 797) | func BenchmarkProxyLogger(b *testing.B) {

FILE: proxy/http_proxy.go
  type HttpStatsHandler (line 25) | type HttpStatsHandler struct
  type HTTPProxy (line 44) | type HTTPProxy struct
    method ServeHTTP (line 80) | func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  type responseWriter (line 262) | type responseWriter struct
    method Header (line 268) | func (rw *responseWriter) Header() http.Header {
    method Write (line 272) | func (rw *responseWriter) Write(b []byte) (int, error) {
    method WriteHeader (line 278) | func (rw *responseWriter) WriteHeader(statusCode int) {
    method Flush (line 283) | func (rw *responseWriter) Flush() {
    method Hijack (line 291) | func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {

FILE: proxy/inetaf_tcpproxy.go
  type childProxy (line 14) | type childProxy struct
  type InetAfTCPProxyServer (line 19) | type InetAfTCPProxyServer struct
    method Close (line 25) | func (tps *InetAfTCPProxyServer) Close() error {
    method Serve (line 52) | func (tps *InetAfTCPProxyServer) Serve(_ net.Listener) error {
    method ServeLater (line 81) | func (tps *InetAfTCPProxyServer) ServeLater(l net.Listener, s Server) {
    method Shutdown (line 85) | func (tps *InetAfTCPProxyServer) Shutdown(ctx context.Context) error {

FILE: proxy/inetaf_tcpproxy_integration_test.go
  function TestProxyTCPAndHTTPS (line 26) | func TestProxyTCPAndHTTPS(t *testing.T) {
  function foundDNSName (line 170) | func foundDNSName(crt *x509.Certificate, dnsName string) bool {

FILE: proxy/listen.go
  function ListenTCP (line 13) | func ListenTCP(l config.Listen, cfg *tls.Config) (net.Listener, error) {
  type tcpListener (line 44) | type tcpListener struct
    method Addr (line 50) | func (ln *tcpListener) Addr() net.Addr {
    method Accept (line 54) | func (ln *tcpListener) Accept() (net.Conn, error) {
    method Close (line 58) | func (ln *tcpListener) Close() error {
  type tcpKeepAliveListener (line 67) | type tcpKeepAliveListener struct
    method Accept (line 71) | func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {

FILE: proxy/listen_test.go
  function TestGracefulShutdown (line 16) | func TestGracefulShutdown(t *testing.T) {

FILE: proxy/serve.go
  type Server (line 23) | type Server interface
  function CloseProxy (line 36) | func CloseProxy(address string) error {
  function Close (line 50) | func Close() {
  function Shutdown (line 59) | func Shutdown(timeout time.Duration) {
  function ListenAndServeHTTP (line 81) | func ListenAndServeHTTP(l config.Listen, h http.Handler, cfg *tls.Config...
  function ListenAndServePrometheus (line 98) | func ListenAndServePrometheus(l config.Listen, pcfg config.Prometheus, c...
  function ListenAndServeHTTPSTCPSNI (line 123) | func ListenAndServeHTTPSTCPSNI(l config.Listen, h http.Handler, p tcp.Ha...
  function ListenAndServeGRPC (line 180) | func ListenAndServeGRPC(l config.Listen, opts []grpc.ServerOption, cfg *...
  function ListenAndServeTCP (line 193) | func ListenAndServeTCP(l config.Listen, h tcp.Handler, cfg *tls.Config) ...
  function serve (line 207) | func serve(ln net.Listener, srv Server) error {

FILE: proxy/tcp/copy_buffer.go
  function copyBuffer (line 11) | func copyBuffer(dst io.Writer, src io.Reader, c gkm.Counter) (err error) {

FILE: proxy/tcp/proxy_proto.go
  function WriteProxyHeader (line 10) | func WriteProxyHeader(out, in net.Conn) error {

FILE: proxy/tcp/server.go
  type Handler (line 15) | type Handler interface
  type HandlerFunc (line 19) | type HandlerFunc
    method ServeTCP (line 21) | func (f HandlerFunc) ServeTCP(in net.Conn) error {
  type Server (line 26) | type Server struct
    method ListenAndServe (line 37) | func (s *Server) ListenAndServe() error {
    method ListenAndServeTLS (line 46) | func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
    method Serve (line 60) | func (s *Server) Serve(l net.Listener) error {
    method closeListeners (line 97) | func (s *Server) closeListeners() {
    method closeConns (line 106) | func (s *Server) closeConns() error {
    method Close (line 116) | func (s *Server) Close() error {
    method Shutdown (line 121) | func (s *Server) Shutdown(ctx context.Context) error {
  type conn (line 130) | type conn struct
    method Read (line 136) | func (c *conn) Read(b []byte) (int, error) {
    method Write (line 143) | func (c *conn) Write(b []byte) (int, error) {
    method Close (line 150) | func (c *conn) Close() error {
    method LocalAddr (line 154) | func (c *conn) LocalAddr() net.Addr {
    method RemoteAddr (line 158) | func (c *conn) RemoteAddr() net.Addr {
    method SetDeadline (line 162) | func (c *conn) SetDeadline(t time.Time) error {
    method SetReadDeadline (line 166) | func (c *conn) SetReadDeadline(t time.Time) error {
    method SetWriteDeadline (line 170) | func (c *conn) SetWriteDeadline(t time.Time) error {

FILE: proxy/tcp/sni_proxy.go
  type SNIProxy (line 19) | type SNIProxy struct
    method ServeTCP (line 39) | func (p *SNIProxy) ServeTCP(in net.Conn) error {

FILE: proxy/tcp/tcp_dynamic_proxy.go
  type DynamicProxy (line 14) | type DynamicProxy struct
    method ServeTCP (line 34) | func (p *DynamicProxy) ServeTCP(in net.Conn) error {

FILE: proxy/tcp/tcp_proxy.go
  type Proxy (line 14) | type Proxy struct
    method ServeTCP (line 34) | func (p *Proxy) ServeTCP(in net.Conn) error {

FILE: proxy/tcp/tcptest/dialer.go
  type Dialer (line 9) | type Dialer interface
  function NewRetryDialer (line 13) | func NewRetryDialer() *RetryDialer {
  type RetryDialer (line 20) | type RetryDialer struct
    method Dial (line 27) | func (d *RetryDialer) Dial(network, addr string) (c net.Conn, err erro...
  function NewTLSRetryDialer (line 42) | func NewTLSRetryDialer(cfg *tls.Config) *TLSRetryDialer {
  type TLSRetryDialer (line 46) | type TLSRetryDialer struct
    method Dial (line 54) | func (d *TLSRetryDialer) Dial(network, addr string) (c net.Conn, err e...
  type dialer (line 69) | type dialer
  function retry (line 71) | func retry(dial dialer, timeout, sleep time.Duration) (c net.Conn, err e...

FILE: proxy/tcp/tcptest/server.go
  type Server (line 15) | type Server struct
    method Start (line 33) | func (s *Server) Start() {
    method StartTLS (line 47) | func (s *Server) StartTLS() {
    method Close (line 77) | func (s *Server) Close() error {
  function NewServer (line 84) | func NewServer(h tcp.Handler) *Server {
  function NewTLSServer (line 90) | func NewTLSServer(h tcp.Handler) *Server {
  function NewUnstartedServer (line 96) | func NewUnstartedServer(h tcp.Handler) *Server {
  function newLocalListener (line 103) | func newLocalListener() net.Listener {
  function NewServerWithProxyProto (line 114) | func NewServerWithProxyProto(h tcp.Handler) *Server {
  function NewTLSServerWithProxyProto (line 120) | func NewTLSServerWithProxyProto(h tcp.Handler) *Server {
  function NewUnstartedServerWithProxyProto (line 126) | func NewUnstartedServerWithProxyProto(h tcp.Handler) *Server {

FILE: proxy/tcp/tls_clienthello.go
  function clientHelloBufferSize (line 13) | func clientHelloBufferSize(data []byte) (int, error) {
  function readServerName (line 55) | func readServerName(clientHelloHandshakeMsg []byte) (serverName string, ...
  constant extensionServerName (line 73) | extensionServerName uint16 = 0
  type clientHelloMsg (line 85) | type clientHelloMsg struct
    method unmarshal (line 100) | func (m *clientHelloMsg) unmarshal(data []byte) bool {

FILE: proxy/tcp/tls_clienthello_test.go
  function TestClientHelloBufferSize (line 8) | func TestClientHelloBufferSize(t *testing.T) {
  function TestReadServerName (line 94) | func TestReadServerName(t *testing.T) {

FILE: proxy/tcp_integration_test.go
  function TestTCPDyanmicProxy (line 36) | func TestTCPDyanmicProxy(t *testing.T) {
  function TestTCPProxy (line 68) | func TestTCPProxy(t *testing.T) {
  function TestTCPProxyWithTLS (line 101) | func TestTCPProxyWithTLS(t *testing.T) {
  function TestTCPSNIProxy (line 172) | func TestTCPSNIProxy(t *testing.T) {
  function testRoundtrip (line 210) | func testRoundtrip(t *testing.T, c net.Conn) {
  function TestTCPProxyWithProxyProto (line 245) | func TestTCPProxyWithProxyProto(t *testing.T) {
  function TestTCPProxyWithTLSWithProxyProto (line 281) | func TestTCPProxyWithTLSWithProxyProto(t *testing.T) {
  function TestTCPSNIProxyWithProxyProto (line 354) | func TestTCPSNIProxyWithProxyProto(t *testing.T) {
  function testProxyProto (line 394) | func testProxyProto(t *testing.T, c net.Conn) {

FILE: proxy/ws_handler.go
  type dialFunc (line 18) | type dialFunc
  function newWSHandler (line 24) | func newWSHandler(host string, dial dialFunc, conn gkm.Gauge) http.Handl...

FILE: proxy/ws_integration_test.go
  function TestProxyWSUpstream (line 18) | func TestProxyWSUpstream(t *testing.T) {
  function testWSEcho (line 96) | func testWSEcho(t *testing.T, url string, hdr http.Header) {
  function wsEchoHandler (line 126) | func wsEchoHandler(ws *websocket.Conn) {

FILE: registry/backend.go
  type Backend (line 3) | type Backend interface

FILE: registry/consul/backend.go
  type be (line 14) | type be struct
    method Register (line 54) | func (b *be) Register(services []string) error {
    method Deregister (line 92) | func (b *be) Deregister(service string) error {
    method DeregisterAll (line 105) | func (b *be) DeregisterAll() error {
    method ManualPaths (line 117) | func (b *be) ManualPaths() ([]string, error) {
    method ReadManual (line 122) | func (b *be) ReadManual(path string) (value string, version uint64, er...
    method WriteManual (line 128) | func (b *be) WriteManual(path string, value string, version uint64) (o...
    method WatchServices (line 138) | func (b *be) WatchServices() chan string {
    method WatchManual (line 148) | func (b *be) WatchManual() chan string {
    method WatchNoRouteHTML (line 156) | func (b *be) WatchNoRouteHTML() chan string {
  function NewBackend (line 21) | func NewBackend(cfg *config.Consul) (registry.Backend, error) {
  function datacenter (line 165) | func datacenter(c *api.Client) (string, error) {
  function stringInSlice (line 182) | func stringInSlice(str string, strSlice []string) bool {

FILE: registry/consul/kv.go
  function watchKV (line 13) | func watchKV(client *api.Client, path string, config chan string, separa...
  function listKeys (line 33) | func listKeys(client *api.Client, path string, waitIndex uint64, require...
  function listKV (line 49) | func listKV(client *api.Client, path string, waitIndex uint64, separator...
  function getKV (line 69) | func getKV(client *api.Client, key string, waitIndex uint64, requireCons...
  function putKV (line 81) | func putKV(client *api.Client, key, value string, index uint64) (bool, e...

FILE: registry/consul/passing.go
  function passingServices (line 12) | func passingServices(checks []*api.HealthCheck, status []string, strict ...
  function isServiceCheck (line 59) | func isServiceCheck(c *api.HealthCheck) bool {
  function hasStatus (line 68) | func hasStatus(c *api.HealthCheck, status []string) bool {

FILE: registry/consul/passing_test.go
  function TestPassingServices (line 10) | func TestPassingServices(t *testing.T) {

FILE: registry/consul/register.go
  constant TTLInterval (line 18) | TTLInterval                       = time.Second * 15
  constant TTLRefreshInterval (line 19) | TTLRefreshInterval                = time.Second * 10
  constant TTLDeregisterCriticalServiceAfter (line 20) | TTLDeregisterCriticalServiceAfter = time.Minute
  function register (line 31) | func register(c *api.Client, service *api.AgentServiceRegistration) chan...
  function serviceRegistration (line 97) | func serviceRegistration(cfg *config.Consul, serviceName string) (*api.A...
  function computeServiceTTLCheckId (line 172) | func computeServiceTTLCheckId(serviceID string) string {

FILE: registry/consul/routecmd.go
  type routecmd (line 16) | type routecmd struct
    method build (line 26) | func (r routecmd) build() []string {
  function parseURLPrefixTag (line 112) | func parseURLPrefixTag(s, prefix string, env map[string]string) (route, ...

FILE: registry/consul/routecmd_test.go
  function TestRouteCmd (line 10) | func TestRouteCmd(t *testing.T) {
  function TestParseTag (line 104) | func TestParseTag(t *testing.T) {

FILE: registry/consul/service.go
  type ServiceMonitor (line 15) | type ServiceMonitor struct
    method Watch (line 33) | func (w *ServiceMonitor) Watch(updates chan string) {
    method makeConfig (line 67) | func (w *ServiceMonitor) makeConfig(checks []*api.HealthCheck) string {
    method serviceConfig (line 111) | func (w *ServiceMonitor) serviceConfig(name string, passing map[string...
  function NewServiceMonitor (line 22) | func NewServiceMonitor(client *api.Client, config *config.Consul, dc str...
  function checksWithTagPrefix (line 146) | func checksWithTagPrefix(prefix string, checks api.HealthChecks) api.Hea...

FILE: registry/custom/backend.go
  type be (line 9) | type be struct
    method Register (line 17) | func (b *be) Register(services []string) error {
    method Deregister (line 21) | func (b *be) Deregister(serviceName string) error {
    method DeregisterAll (line 25) | func (b *be) DeregisterAll() error {
    method ManualPaths (line 29) | func (b *be) ManualPaths() ([]string, error) {
    method ReadManual (line 33) | func (b *be) ReadManual(string) (value string, version uint64, err err...
    method WriteManual (line 37) | func (b *be) WriteManual(path string, value string, version uint64) (o...
    method WatchServices (line 41) | func (b *be) WatchServices() chan string {
    method WatchManual (line 49) | func (b *be) WatchManual() chan string {
    method WatchNoRouteHTML (line 53) | func (b *be) WatchNoRouteHTML() chan string {
  function NewBackend (line 13) | func NewBackend(cfg *config.Custom) (registry.Backend, error) {

FILE: registry/custom/custom.go
  function customRoutes (line 14) | func customRoutes(cfg *config.Custom, ch chan string) {

FILE: registry/custom/custom_test.go
  function TestCustomRoutes (line 13) | func TestCustomRoutes(t *testing.T) {
  function handleTest (line 47) | func handleTest(w http.ResponseWriter, r *http.Request) {

FILE: registry/file/backend.go
  function NewBackend (line 14) | func NewBackend(cfg *config.File) (registry.Backend, error) {

FILE: registry/static/backend.go
  type be (line 10) | type be struct
    method Register (line 18) | func (b *be) Register(services []string) error {
    method Deregister (line 22) | func (b *be) Deregister(serviceName string) error {
    method DeregisterAll (line 26) | func (b *be) DeregisterAll() error {
    method ManualPaths (line 30) | func (b *be) ManualPaths() ([]string, error) {
    method ReadManual (line 34) | func (b *be) ReadManual(string) (value string, version uint64, err err...
    method WriteManual (line 38) | func (b *be) WriteManual(path string, value string, version uint64) (o...
    method WatchServices (line 42) | func (b *be) WatchServices() chan string {
    method WatchManual (line 48) | func (b *be) WatchManual() chan string {
    method WatchNoRouteHTML (line 52) | func (b *be) WatchNoRouteHTML() chan string {
  function NewBackend (line 14) | func NewBackend(cfg *config.Static) (registry.Backend, error) {

FILE: rootwarn_unix.go
  constant interval (line 13) | interval = time.Hour
  constant warnInsecure (line 15) | warnInsecure = `
  constant warn17behavior (line 25) | warn17behavior = `
  function WarnIfRunAsRoot (line 36) | func WarnIfRunAsRoot(allowRoot bool) {
  function doWarn (line 47) | func doWarn(allowRoot bool) {
  function remind (line 56) | func remind(allowRoot bool) {

FILE: rootwarn_windows.go
  function WarnIfRunAsRoot (line 6) | func WarnIfRunAsRoot(allowRoot bool) {

FILE: route/access_rules.go
  constant ipAllowTag (line 13) | ipAllowTag = "allow:ip"
  constant ipDenyTag (line 14) | ipDenyTag  = "deny:ip"
  method AccessDeniedHTTP (line 18) | func (t *Target) AccessDeniedHTTP(r *http.Request) bool {
  method AccessDeniedTCP (line 70) | func (t *Target) AccessDeniedTCP(c net.Conn) bool {
  method denyByIP (line 92) | func (t *Target) denyByIP(ip net.IP) bool {
  method ProcessAccessRules (line 148) | func (t *Target) ProcessAccessRules() error {
  method parseAccessRule (line 163) | func (t *Target) parseAccessRule(allowDeny string) error {

FILE: route/access_rules_test.go
  function TestAccessRules_parseAccessRule (line 10) | func TestAccessRules_parseAccessRule(t *testing.T) {
  function TestAccessRules_denyByIP (line 67) | func TestAccessRules_denyByIP(t *testing.T) {
  function TestAccessRules_AccessDeniedHTTP (line 158) | func TestAccessRules_AccessDeniedHTTP(t *testing.T) {

FILE: route/auth.go
  method Authorized (line 10) | func (t *Target) Authorized(r *http.Request, w http.ResponseWriter, auth...

FILE: route/auth_test.go
  type testAuth (line 11) | type testAuth struct
    method Authorized (line 15) | func (t *testAuth) Authorized(r *http.Request, w http.ResponseWriter) ...
  type responseWriter (line 19) | type responseWriter struct
    method Header (line 25) | func (rw *responseWriter) Header() http.Header {
    method Write (line 29) | func (rw *responseWriter) Write(b []byte) (int, error) {
    method WriteHeader (line 34) | func (rw *responseWriter) WriteHeader(statusCode int) {
  function TestTarget_Authorized (line 38) | func TestTarget_Authorized(t *testing.T) {

FILE: route/glob_cache.go
  type GlobCache (line 9) | type GlobCache struct
    method Get (line 33) | func (c *GlobCache) Get(pattern string) (glob.Glob, error) {
  function NewGlobCache (line 24) | func NewGlobCache(size int) *GlobCache {

FILE: route/glob_cache_test.go
  function TestGlobCache (line 9) | func TestGlobCache(t *testing.T) {

FILE: route/issue57_test.go
  function TestIssue57 (line 12) | func TestIssue57(t *testing.T) {

FILE: route/matcher.go
  type matcher (line 8) | type matcher
  function prefixMatcher (line 19) | func prefixMatcher(uri string, r *Route) bool {
  function globMatcher (line 24) | func globMatcher(uri string, r *Route) bool {
  function iPrefixMatcher (line 29) | func iPrefixMatcher(uri string, r *Route) bool {

FILE: route/matcher_test.go
  function TestPrefixMatcher (line 9) | func TestPrefixMatcher(t *testing.T) {
  function TestGlobMatcher (line 30) | func TestGlobMatcher(t *testing.T) {
  function TestIPrefixMatcher (line 64) | func TestIPrefixMatcher(t *testing.T) {

FILE: route/metrics_cleanup_test.go
  function TestMetricsCleanup (line 14) | func TestMetricsCleanup(t *testing.T) {
  type testDeletableHistogram (line 139) | type testDeletableHistogram struct
    method Observe (line 144) | func (h *testDeletableHistogram) Observe(v float64) {
    method With (line 148) | func (h *testDeletableHistogram) With(labelValues ...string) gkm.Histo...
    method DeleteLabelValues (line 155) | func (h *testDeletableHistogram) DeleteLabelValues(labelValues ...stri...
  type testDeletableCounter (line 160) | type testDeletableCounter struct
    method Add (line 165) | func (c *testDeletableCounter) Add(v float64) {
    method With (line 169) | func (c *testDeletableCounter) With(labelValues ...string) gkm.Counter {
    method DeleteLabelValues (line 176) | func (c *testDeletableCounter) DeleteLabelValues(labelValues ...string...
  function extractValues (line 181) | func extractValues(lvs []string) []string {
  function TestMetricsCleanupViaMultiProvider (line 195) | func TestMetricsCleanupViaMultiProvider(t *testing.T) {
  type testPromProvider (line 283) | type testPromProvider struct
    method NewCounter (line 287) | func (p *testPromProvider) NewCounter(name string, labels ...string) g...
    method NewGauge (line 296) | func (p *testPromProvider) NewGauge(name string, labels ...string) gkm...
    method NewHistogram (line 305) | func (p *testPromProvider) NewHistogram(name string, labels ...string)...
  function sanitizeName (line 316) | func sanitizeName(name string) string {
  type testDeletableGauge (line 321) | type testDeletableGauge struct
    method Set (line 326) | func (g *testDeletableGauge) Set(v float64) {
    method Add (line 330) | func (g *testDeletableGauge) Add(v float64) {
    method With (line 334) | func (g *testDeletableGauge) With(labelValues ...string) gkm.Gauge {
    method DeleteLabelValues (line 341) | func (g *testDeletableGauge) DeleteLabelValues(labelValues ...string) ...

FILE: route/parse_new.go
  constant Commands (line 21) | Commands = `
  function Parse (line 71) | func Parse(in *bytes.Buffer) (defs []*RouteDef, err error) {
  function ParseAliases (line 101) | func ParseAliases(in string) (names []string, err error) {
  function parseRouteAdd (line 140) | func parseRouteAdd(s string) (*RouteDef, error) {
  function parseRouteDel (line 168) | func parseRouteDel(s string) (*RouteDef, error) {
  function parseRouteWeight (line 189) | func parseRouteWeight(s string) (*RouteDef, error) {
  function mustCompileWithFlexibleSpace (line 212) | func mustCompileWithFlexibleSpace(re string) *regexp.Regexp {
  function parseWeight (line 216) | func parseWeight(s string) (float64, error) {
  function parseTags (line 227) | func parseTags(s string) []string {
  function parseOpts (line 238) | func parseOpts(s string) map[string]string {

FILE: route/parse_test.go
  function TestParse (line 10) | func TestParse(t *testing.T) {
  function TestParseAliases (line 191) | func TestParseAliases(t *testing.T) {

FILE: route/picker.go
  type picker (line 9) | type picker
  function rndPicker (line 19) | func rndPicker(r *Route) *Target {
  function rrPicker (line 24) | func rrPicker(r *Route) *Target {

FILE: route/picker_test.go
  function mustParse (line 15) | func mustParse(rawurl string) *url.URL {
  function TestRndPicker (line 23) | func TestRndPicker(t *testing.T) {
  function TestRRPicker (line 47) | func TestRRPicker(t *testing.T) {
  function BenchmarkOldRandIntn (line 72) | func BenchmarkOldRandIntn(b *testing.B) {
  function BenchmarkMathRandIntn (line 79) | func BenchmarkMathRandIntn(b *testing.B) {

FILE: route/route.go
  type Route (line 22) | type Route struct
    method addTarget (line 46) | func (r *Route) addTarget(service string, targetURL *url.URL, fixedWei...
    method filter (line 106) | func (r *Route) filter(skip func(t *Target) bool) {
    method setWeight (line 118) | func (r *Route) setWeight(service string, weight float64, tags []strin...
    method TargetConfig (line 165) | func (r *Route) TargetConfig(t *Target, addWeight bool) string {
    method config (line 193) | func (r *Route) config(addWeight bool) []string {
    method weighTargets (line 218) | func (r *Route) weighTargets() {
  function contains (line 149) | func contains(src, dst []string) bool {
  constant maxSlots (line 208) | maxSlots = 1e4
  type byN (line 324) | type byN
    method Len (line 326) | func (r byN) Len() int           { return len(r) }
    method Swap (line 327) | func (r byN) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
    method Less (line 328) | func (r byN) Less(i, j int) bool { return r[i].n < r[j].n }

FILE: route/route_bench_test.go
  function initRoutes (line 22) | func initRoutes() {
  function BenchmarkPrefixMatcherRndPicker5Routes (line 29) | func BenchmarkPrefixMatcherRndPicker5Routes(b *testing.B) {
  function BenchmarkPrefixMatcherRRPicker5Routes (line 35) | func BenchmarkPrefixMatcherRRPicker5Routes(b *testing.B) {
  function BenchmarkPrefixMatcherRndPicker10Routes (line 41) | func BenchmarkPrefixMatcherRndPicker10Routes(b *testing.B) {
  function BenchmarkPrefixMatcherRRPicker10Routes (line 48) | func BenchmarkPrefixMatcherRRPicker10Routes(b *testing.B) {
  function BenchmarkPrefixMatcherRndPicker100Routes (line 55) | func BenchmarkPrefixMatcherRndPicker100Routes(b *testing.B) {
  function BenchmarkPrefixMatcherRRPicker100Routes (line 62) | func BenchmarkPrefixMatcherRRPicker100Routes(b *testing.B) {
  function BenchmarkPrefixMatcherRndPicker500Routes (line 69) | func BenchmarkPrefixMatcherRndPicker500Routes(b *testing.B) {
  function BenchmarkPrefixMatcherRRPicker500Routes (line 76) | func BenchmarkPrefixMatcherRRPicker500Routes(b *testing.B) {
  function makeRoutes (line 88) | func makeRoutes(domains, paths, depth, urls int) Table {
  function makeRequests (line 111) | func makeRequests(t Table) []*http.Request {
  function benchmarkGet (line 124) | func benchmarkGet(t Table, match matcher, pick picker, pb *testing.PB) {

FILE: route/route_def.go
  type Cmd (line 3) | type Cmd
  constant RouteAddCmd (line 6) | RouteAddCmd    Cmd = "route add"
  constant RouteDelCmd (line 7) | RouteDelCmd    Cmd = "route del"
  constant RouteWeightCmd (line 8) | RouteWeightCmd Cmd = "route weight"
  type RouteDef (line 11) | type RouteDef struct

FILE: route/routes.go
  type Routes (line 4) | type Routes
    method find (line 7) | func (rt Routes) find(path string) *Route {
    method Len (line 17) | func (rt Routes) Len() int           { return len(rt) }
    method Swap (line 18) | func (rt Routes) Swap(i, j int)      { rt[i], rt[j] = rt[j], rt[i] }
    method Less (line 19) | func (rt Routes) Less(i, j int) bool { return rt[j].Path < rt[i].Path }

FILE: route/table.go
  function init (line 32) | func init() {
  type metrix (line 38) | type metrix struct
  function makeMetricKey (line 47) | func makeMetricKey(service, host, path, target string) string {
  function parseMetricKey (line 52) | func parseMetricKey(key string) (service, host, path, target string) {
  function collectTableMetricKeys (line 61) | func collectTableMetricKeys(t Table) map[string]struct{} {
  function cleanupStaleMetrics (line 76) | func cleanupStaleMetrics(oldTable, newTable Table) {
  function SetMetricsProvider (line 104) | func SetMetricsProvider(p metrics.Provider) {
  function GetTable (line 113) | func GetTable() Table {
  function SetTable (line 121) | func SetTable(t Table) {
  type Table (line 139) | type Table
    method addRoute (line 215) | func (t Table) addRoute(d *RouteDef) error {
    method weighRoute (line 261) | func (t Table) weighRoute(d *RouteDef) error {
    method delRoute (line 285) | func (t Table) delRoute(d *RouteDef) error {
    method route (line 352) | func (t Table) route(host, path string) *Route {
    method matchingHosts (line 378) | func (t Table) matchingHosts(req *http.Request, globCache *GlobCache) ...
    method matchingHostNoGlob (line 405) | func (t Table) matchingHostNoGlob(req *http.Request) (hosts []string) {
    method Lookup (line 465) | func (t Table) Lookup(req *http.Request, pick picker, match matcher, g...
    method LookupHost (line 499) | func (t Table) LookupHost(host string, pick picker) *Target {
    method lookup (line 503) | func (t Table) lookup(host, path string, pick picker, match matcher) *...
    method config (line 524) | func (t Table) config(addWeight bool) []string {
    method String (line 547) | func (t Table) String() string {
    method Dump (line 552) | func (t Table) Dump() string {
  function hostpath (line 144) | func hostpath(prefix string) (host string, path string) {
  function NewTable (line 156) | func NewTable(b *bytes.Buffer) (t Table, err error) {
  function NewTableCustom (line 187) | func NewTableCustom(defs *[]RouteDef) (t Table, err error) {
  function normalizeHost (line 362) | func normalizeHost(host string, tls bool) string {
  function normalizeHostNoLower (line 366) | func normalizeHostNoLower(host string, tls bool) string {
  function sortHostsReverseHostPort (line 418) | func sortHostsReverseHostPort(hosts []string) []string {
  function ReverseHostPort (line 442) | func ReverseHostPort(s string) string {

FILE: route/table_test.go
  constant globEnabled (line 18) | globEnabled  = false
  constant globDisabled (line 19) | globDisabled = true
  function TestTableParse (line 25) | func TestTableParse(t *testing.T) {
  function TestNormalizeHost (line 477) | func TestNormalizeHost(t *testing.T) {
  function TestTableLookupIssue448 (line 499) | func TestTableLookupIssue448(t *testing.T) {
  function TestTableLookup (line 572) | func TestTableLookup(t *testing.T) {
  function TestTableLookup_656 (line 648) | func TestTableLookup_656(t *testing.T) {
  function TestNewTableCustom (line 674) | func TestNewTableCustom(t *testing.T) {
  function TestTable_Dump (line 738) | func TestTable_Dump(t *testing.T) {

FILE: route/target.go
  type Target (line 10) | type Target struct
    method BuildRedirectURL (line 76) | func (t *Target) BuildRedirectURL(requestURL *url.URL) {

FILE: route/target_test.go
  function TestTarget_BuildRedirectURL (line 9) | func TestTarget_BuildRedirectURL(t *testing.T) {

FILE: transport/transport.go
  function NewTransport (line 14) | func NewTransport(tlscfg *tls.Config) *http.Transport {
  function SetConfig (line 27) | func SetConfig(ncfg *config.Config) {

FILE: uuid/format.go
  function ToString (line 13) | func ToString(u [24]byte) string {

FILE: uuid/uuid.go
  function NewUUID (line 10) | func NewUUID() string {
Condensed preview — 348 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,337K chars).
[
  {
    "path": ".dockerignore",
    "chars": 12,
    "preview": "fabio\ndist/\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 23,
    "preview": "* @fabiolb/maintainers\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 1034,
    "preview": "on: [push, pull_request]\nname: Build\npermissions:\n  contents: read\njobs:\n  golangci:\n    name: lint\n    runs-on: ubuntu-"
  },
  {
    "path": ".github/workflows/github-pages.yml",
    "chars": 528,
    "preview": "name: github pages\npermissions:\n  contents: write\non:\n  push:\n    branches:\n      - master\njobs:\n  build-deploy:\n    run"
  },
  {
    "path": ".github/workflows/go-releaser.yml",
    "chars": 1313,
    "preview": "name: goreleaser\npermissions:\n  contents: write\non:\n  push:\n    tags:\n      - '*'\njobs:\n  goreleaser:\n    runs-on: ubunt"
  },
  {
    "path": ".gitignore",
    "chars": 227,
    "preview": "*-amd64\n*.orig\n*.out\n*.p12\n*.pem\n*.pprof\n*.sha256\n*.swp\n*.tar.gz\n*.test\n*.un~\n*.zip\n.DS_Store\n.idea\n.vagrant\nbuild/build"
  },
  {
    "path": ".golangci.yml",
    "chars": 1987,
    "preview": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE f"
  },
  {
    "path": ".goreleaser.yml",
    "chars": 2873,
    "preview": "version: 2\nbuilds:\n  - binary: fabio\n    env:\n      - CGO_ENABLED=0\n    goos:\n      - darwin\n      - linux\n      - freeb"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 85070,
    "preview": "# Changelog\n\n## [v1.6.10](https://github.com/fabiolb/fabio/tree/v1.6.10) (2025-11-24)\n\n[Full Changelog](https://github.c"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 279,
    "preview": "# Code of Conduct\n\nPlease respect others and treat them the way you want to\nbe treated yourself.\n\nIf you feel someone ov"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 2640,
    "preview": "# Contributing Guidelines\n\nMore information on how to contribute to fabio can be found on \nthe [wiki](https://github.com"
  },
  {
    "path": "Dockerfile",
    "chars": 1252,
    "preview": "FROM golang AS build\n\nARG TARGETARCH\nARG consul_version=1.22.0\nADD https://releases.hashicorp.com/consul/${consul_versio"
  },
  {
    "path": "Dockerfile-goreleaser",
    "chars": 671,
    "preview": "FROM debian:stable-slim AS build\nRUN apt-get update && apt-get install -y git ca-certificates libcap2-bin\nADD fabio /usr"
  },
  {
    "path": "LICENSE",
    "chars": 1389,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2020 Education Networks of America.  All rights reserved.\nCopyright (c) 2017-2020 F"
  },
  {
    "path": "Makefile",
    "chars": 6736,
    "preview": "# CUR_TAG is the last git tag plus the delta from the current commit to the tag\n# e.g. v1.5.5-<nr of commits since>-g<cu"
  },
  {
    "path": "NOTICES.txt",
    "chars": 5609,
    "preview": "fabio\n\nhttps://github.com/fabiolb/fabio\nLicense: MIT (https://github.com/fabiolb/fabio/LICENSE)\nCopyright (c) 2017 Frank"
  },
  {
    "path": "README.md",
    "chars": 8976,
    "preview": "<p align=\"center\">\n  <p align=\"center\" style=\"width: 50%; height: 64px;\">\n    <img src=\"https://cdn.rawgit.com/fabiolb/f"
  },
  {
    "path": "admin/api/api.go",
    "chars": 525,
    "preview": "// Package api provides the HTTP api.\npackage api\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc writeJSON(w http"
  },
  {
    "path": "admin/api/config.go",
    "chars": 188,
    "preview": "package api\n\nimport \"net/http\"\n\ntype ConfigHandler struct {\n\tConfig interface{}\n}\n\nfunc (h *ConfigHandler) ServeHTTP(w h"
  },
  {
    "path": "admin/api/manual.go",
    "chars": 1393,
    "preview": "package api\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/fabiolb/fabio/registry\"\n)\n\n// ManualHandler prov"
  },
  {
    "path": "admin/api/paths.go",
    "chars": 738,
    "preview": "package api\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/fabiolb/fabio/registry\"\n)\n\ntype ManualPathsHandler str"
  },
  {
    "path": "admin/api/routes.go",
    "chars": 1487,
    "preview": "package api\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/fabiolb/fabio/route\"\n)\n\ntype RoutesHandler str"
  },
  {
    "path": "admin/api/version.go",
    "chars": 204,
    "preview": "package api\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\ntype VersionHandler struct {\n\tVersion string\n}\n\nfunc (h *VersionHandler) Ser"
  },
  {
    "path": "admin/server.go",
    "chars": 2415,
    "preview": "package admin\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/fabiolb/fabio/admin/api\"\n\t\"github.com/"
  },
  {
    "path": "admin/server_test.go",
    "chars": 1575,
    "preview": "package admin\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/fabiolb/fabio/config\"\n)\n\nfunc TestAdmi"
  },
  {
    "path": "admin/ui/assets/fonts/material-icons.css",
    "chars": 970,
    "preview": "@font-face {\n  font-family: 'Material Icons';\n  font-style: normal;\n  font-weight: 400;\n  src: url(MaterialIcons-Regular"
  },
  {
    "path": "admin/ui/generate.go",
    "chars": 1249,
    "preview": "package ui\n\n//go:generate rm -rf assets/code.jquery.com\n//go:generate rm -rf assets/cdnjs.cloudflare.com\n//go:generate w"
  },
  {
    "path": "admin/ui/manual.go",
    "chars": 4214,
    "preview": "package ui\n\nimport (\n\t\"html/template\"\n\t\"net/http\"\n)\n\ntype ManualHandler struct {\n\tBasePath string\n\tColor    string\n\tTitl"
  },
  {
    "path": "admin/ui/route.go",
    "chars": 6102,
    "preview": "package ui\n\nimport (\n\t\"html/template\"\n\t\"net/http\"\n\n\t\"github.com/fabiolb/fabio/config\"\n)\n\n// RoutesHandler provides the U"
  },
  {
    "path": "admin/ui/static.go",
    "chars": 68,
    "preview": "package ui\n\nimport \"embed\"\n\n//go:embed assets/*\nvar Static embed.FS\n"
  },
  {
    "path": "assert/assert.go",
    "chars": 591,
    "preview": "// Package assert provides a simple assert framework.\npackage assert\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runt"
  },
  {
    "path": "auth/auth.go",
    "chars": 563,
    "preview": "package auth\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/fabiolb/fabio/config\"\n)\n\ntype AuthScheme interface {\n\tAuthorized"
  },
  {
    "path": "auth/auth_test.go",
    "chars": 1637,
    "preview": "package auth\n\nimport (\n\t\"testing\"\n\n\t\"github.com/fabiolb/fabio/config\"\n)\n\nfunc TestLoadAuthSchemes(t *testing.T) {\n\n\tt.Ru"
  },
  {
    "path": "auth/basic.go",
    "chars": 2001,
    "preview": "package auth\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/fabiolb/fabio/config\"\n\t\"github.com/tg123/"
  },
  {
    "path": "auth/basic_test.go",
    "chars": 4861,
    "preview": "package auth\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.co"
  },
  {
    "path": "bgp/bgp_nonwindows.go",
    "chars": 10892,
    "preview": "//go:build !windows\n// +build !windows\n\npackage bgp\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\n\t\"github."
  },
  {
    "path": "bgp/bgp_nonwindows_test.go",
    "chars": 4196,
    "preview": "//go:build !windows\n// +build !windows\n\npackage bgp\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"github.com/fabiolb/fabio/con"
  },
  {
    "path": "bgp/bgp_windows.go",
    "chars": 382,
    "preview": "package bgp\n\nimport (\n\t\"errors\"\n\t\"github.com/fabiolb/fabio/config\"\n)\n\ntype BGPHandler struct{}\n\nvar ErrNoWindows = error"
  },
  {
    "path": "bgp/logger.go",
    "chars": 1643,
    "preview": "package bgp\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\n\t\"github.com/fabiolb/fabio/exit\"\n\t\"github.com/fabiolb/fabio/logger\"\n\n\tbg"
  },
  {
    "path": "bgp/test_data/bgp.toml",
    "chars": 324,
    "preview": "[[neighbors]]\n  [neighbors.config]\n    neighbor-address = \"127.0.0.2\"\n    peer-as = 65000\n  [neighbors.transport.config]"
  },
  {
    "path": "build/ca-certificates.crt",
    "chars": 273790,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE\nAwwNQUNFRElDT00gUm9vdDEMMAo"
  },
  {
    "path": "build/homebrew.sh",
    "chars": 1102,
    "preview": "#!/bin/bash\n#\n# homebrew.sh creates an updated homebrew on fabio\n\nset -o nounset\nset -o errexit\nset -o pipefail\n\nreadonl"
  },
  {
    "path": "build/homebrew.vim",
    "chars": 156,
    "preview": "/url\nDAurl \"https://github.com/fabiolb/fabio/archive/v1.6.3.tar.gz\"\u001b/sha256\nDAsha256 \"e85b70a700652b051260b8c49ce63d21d2"
  },
  {
    "path": "build/issue-225-gen-cert.bash",
    "chars": 3212,
    "preview": "#!/bin/bash\n#\n# This script addresses issue #225 (https://github.com/fabiolb/fabio/issues/225)\n# and generates a number "
  },
  {
    "path": "build/releasenotes.pl",
    "chars": 123,
    "preview": "#!/usr/bin/env perl\n\nuse strict;\n\nlocal $/;\n$_ = <>;\nif (/^### \\[$ENV{RELEASE}.*?\\n\\s*(.*?)^### \\[v/gms) {\n    print $1;"
  },
  {
    "path": "build/tag.sh",
    "chars": 833,
    "preview": "#!/bin/bash -e\n#\n# Script for replacing the version number\n# in main.go, committing and tagging the code\n\nreadonly prgdi"
  },
  {
    "path": "build/update-ssl.sh",
    "chars": 198,
    "preview": "#!/bin/bash -e \n#\n# Script for updating ca-certificates.crt\n\nprgdir=$(cd $(dirname $0) ; pwd)\n\necho \"Updating certificat"
  },
  {
    "path": "cert/consul_source.go",
    "chars": 3578,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/url\"\n\t\"path\"\n\t\"reflect\"\n\t\"strings\"\n\t\"t"
  },
  {
    "path": "cert/consul_source_test.go",
    "chars": 1705,
    "preview": "package cert\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/hashicorp/consul/api\"\n)\n\nfunc TestParseConsulURL(t *testing.T"
  },
  {
    "path": "cert/file_source.go",
    "chars": 1352,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"os\"\n\n\t\"github.com/fabiolb/fabio/exit\"\n)\n\n// FileSource implements "
  },
  {
    "path": "cert/http_source.go",
    "chars": 931,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"time\"\n)\n\n// HTTPSource implements a certificate source which loads"
  },
  {
    "path": "cert/load.go",
    "chars": 5109,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/url\""
  },
  {
    "path": "cert/load_test.go",
    "chars": 3394,
    "preview": "package cert\n\nimport (\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"testing\"\n)\n\nfunc TestBase(t *testing.T) {\n\ttests := []struct {\n\t"
  },
  {
    "path": "cert/path_source.go",
    "chars": 847,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"path/filepath\"\n\t\"time\"\n)\n\nconst (\n\tDefaultCertPath     = \"cert\"\n\tD"
  },
  {
    "path": "cert/source.go",
    "chars": 3919,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\n\t\"github.com/fabiolb/fabio/config\"\n\t\"golang.org/x/sync/singl"
  },
  {
    "path": "cert/source_test.go",
    "chars": 20876,
    "preview": "package cert\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding"
  },
  {
    "path": "cert/store.go",
    "chars": 2717,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"log\"\n\t\"strings\"\n\t\"sync/atomic\"\n)\n\n// Store provides a dy"
  },
  {
    "path": "cert/store_test.go",
    "chars": 2734,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestGetCertificate(t *testing.T) {\n"
  },
  {
    "path": "cert/vault_client.go",
    "chars": 5226,
    "preview": "package cert\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/hashicorp/vault/"
  },
  {
    "path": "cert/vault_pki_source.go",
    "chars": 3654,
    "preview": "package cert\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"math/big\"\n\t\"sync\"\n\t\"time\"\n"
  },
  {
    "path": "cert/vault_source.go",
    "chars": 4270,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"log\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/hashicorp/vau"
  },
  {
    "path": "cert/watch.go",
    "chars": 886,
    "preview": "package cert\n\nimport (\n\t\"crypto/tls\"\n\t\"log\"\n\t\"reflect\"\n\t\"time\"\n)\n\n// watch monitors the result of the loadFn function fo"
  },
  {
    "path": "config/config.go",
    "chars": 5067,
    "preview": "package config\n\nimport (\n\t\"net/http\"\n\t\"regexp\"\n\t\"time\"\n)\n\ntype Config struct {\n\tLog                  Log\n\tProfileMode   "
  },
  {
    "path": "config/default.go",
    "chars": 3186,
    "preview": "package config\n\nimport (\n\t\"os\"\n\t\"runtime\"\n\t\"time\"\n)\n\nvar defaultValues = struct {\n\tListenerValue         string\n\tCertSou"
  },
  {
    "path": "config/flagset.go",
    "chars": 3225,
    "preview": "package config\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/magiconair/properties\"\n)\n\n// -- stringSliceV"
  },
  {
    "path": "config/flagset_test.go",
    "chars": 2209,
    "preview": "package config\n\nimport (\n\t\"flag\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/magiconair/properties\"\n)\n\nfunc TestParseFlags(t *te"
  },
  {
    "path": "config/kvslice.go",
    "chars": 4948,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// parseKVSlice parses a configuration string in the form\n//"
  },
  {
    "path": "config/kvslice_test.go",
    "chars": 2131,
    "preview": "package config\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestParseKVSlice(t *testing.T) {\n\ttests := []struct {\n\t\tdesc stri"
  },
  {
    "path": "config/load.go",
    "chars": 32246,
    "preview": "package config\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"str"
  },
  {
    "path": "config/load_test.go",
    "chars": 32737,
    "preview": "package config\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"st"
  },
  {
    "path": "config/localip.go",
    "chars": 699,
    "preview": "package config\n\nimport (\n\t\"log\"\n\t\"net\"\n)\n\n// LocalIP tries to determine a non-loopback address for the local machine\nfun"
  },
  {
    "path": "demo/aws/.gitignore",
    "chars": 27,
    "preview": "*.tfstate\n*.tfstate.backup\n"
  },
  {
    "path": "demo/aws/main.tf",
    "chars": 4397,
    "preview": "# Specify the provider and access details\nprovider \"aws\" {\n  region = \"${var.aws_region}\"\n}\n\n# Create a VPC to launch ou"
  },
  {
    "path": "demo/aws/outputs.tf",
    "chars": 57,
    "preview": "output \"address\" {\n  value = \"${aws_elb.web.dns_name}\"\n}\n"
  },
  {
    "path": "demo/aws/variables.tf",
    "chars": 321,
    "preview": "variable \"public_key_path\" {\n  default = \"~/.ssh/terraform.pub\"\n}\n\nvariable \"key_name\" {\n  default = \"terraform\"\n}\n\nvari"
  },
  {
    "path": "docs/.gitignore",
    "chars": 8,
    "preview": "public/\n"
  },
  {
    "path": "docs/README.md",
    "chars": 593,
    "preview": "# fabiolb.net website\n\nThis is the source code for the https://fabiolb.net website.\n\nIt is built with [Hugo](https://goh"
  },
  {
    "path": "docs/archetypes/default.md",
    "chars": 98,
    "preview": "---\ntitle: \"{{ replace .TranslationBaseName \"-\" \" \" | title }}\"\ndate: {{ .Date }}\ndraft: true\n---\n"
  },
  {
    "path": "docs/config.toml",
    "chars": 230,
    "preview": "baseURL      = \"https://fabiolb.net/\"\nlanguageCode = \"en-us\"\ntitle        = \"fabio - The Consul Load-Balancer\"\ndisableKi"
  },
  {
    "path": "docs/content/_index.md",
    "chars": 1220,
    "preview": "---\ntitle: \"Overview\"\n---\n\nFabio is an HTTP and TCP reverse proxy that configures itself with data from\n[Consul](https:/"
  },
  {
    "path": "docs/content/cfg/_index.md",
    "chars": 5947,
    "preview": "---\ntitle: \"Config Language\"\nweight: 550\n---\n\nThe routing table is configured with commands in the language specified be"
  },
  {
    "path": "docs/content/code-of-conduct/_index.md",
    "chars": 156,
    "preview": "---\ntitle: \"Code of Conduct\"\nweight: 1000\n---\n\nBe nice and treat others with respect.\n\nPlease contact a project owner if"
  },
  {
    "path": "docs/content/contact/_index.md",
    "chars": 243,
    "preview": "---\ntitle: \"Contact\"\nweight: 2000\n---\n\nPlease create a [GitHub](https://github.com/fabiolb/fabio/issues) issue for all f"
  },
  {
    "path": "docs/content/contrib/_index.md",
    "chars": 303,
    "preview": "---\ntitle: \"Contributing\"\nweight: 900\n---\n\nContributions to fabio of any kind are welcome including documentation, examp"
  },
  {
    "path": "docs/content/contrib/development.md",
    "chars": 790,
    "preview": "---\ntitle: \"Development\"\nweight: 200\n---\n\nFor newcomers to Go, you can't just `git clone` your forked repo and work from"
  },
  {
    "path": "docs/content/contrib/guidelines.md",
    "chars": 1258,
    "preview": "---\ntitle: \"Guidelines\"\nweight: 100\n---\n\n### Your contribution is welcome!\n\nTo make merging code as seamless as possible"
  },
  {
    "path": "docs/content/deploy/_index.md",
    "chars": 714,
    "preview": "---\ntitle: \"Deployment\"\nweight: 300\n---\n\nThe main use-case for fabio is to distribute incoming HTTP(S) and TCP requests\n"
  },
  {
    "path": "docs/content/deploy/amazon-api-gw.md",
    "chars": 1989,
    "preview": "---\ntitle: \"Amazon API Gateway\"\nweight: 500\n---\n\nYou can deploy fabio as the target of an [Amazon API Gateway](https://a"
  },
  {
    "path": "docs/content/deploy/amazon-elb.md",
    "chars": 674,
    "preview": "---\ntitle: \"Amazon ELB\"\nweight: 400\n---\n\nYou can deploy fabio behind an Amazon ELB and enable [PROXY protocol support](h"
  },
  {
    "path": "docs/content/deploy/direct.md",
    "chars": 904,
    "preview": "---\ntitle: \"Direct\"\nweight: 100\n---\n\nIn the following setup fabio is configured to listen on the public ip(s)\nwhere it c"
  },
  {
    "path": "docs/content/deploy/existing-lb.md",
    "chars": 1036,
    "preview": "---\ntitle: \"Behind existing Gateway\"\nweight: 200\n---\n\nIn the following setup fabio is configured receive all incoming tr"
  },
  {
    "path": "docs/content/faq/_index.md",
    "chars": 33,
    "preview": "---\ntitle: \"FAQ\"\nweight: 800\n---\n"
  },
  {
    "path": "docs/content/faq/binding-to-low-ports.md",
    "chars": 2955,
    "preview": "---\ntitle: \"Binding to Low Ports\"\n---\n\nIf you want to bind fabio to ports below 1024 - so called privileged ports -\nwith"
  },
  {
    "path": "docs/content/faq/multiple-protocol-listeners.md",
    "chars": 694,
    "preview": "---\ntitle: \"Handling Multiple Protocols\"\n---\n\nIt is quite possible for a single fabio instance to serve multiple protoco"
  },
  {
    "path": "docs/content/faq/request-debugging.md",
    "chars": 360,
    "preview": "---\ntitle: \"Test fabio with curl\"\n---\n\n##### How do I send a request to fabio via `curl`?\n\n```\ncurl -v -H 'Host: foo.com"
  },
  {
    "path": "docs/content/faq/verifying-releases.md",
    "chars": 8397,
    "preview": "---\ntitle: \"Verifying Releases\"\n---\n\nfabio releases can be verified by comparing the SHA256 checksum\nand by verifying th"
  },
  {
    "path": "docs/content/faq/why-fabio.md",
    "chars": 269,
    "preview": "---\ntitle: \"Why 'Fabio'?\"\n---\n\nWhen I was writing fabio my son was watching \"Finding Nemo\" almost every day and Dory kee"
  },
  {
    "path": "docs/content/feature/_index.md",
    "chars": 2226,
    "preview": "---\ntitle: \"Features\"\nweight: 200\n---\n\nThe following list provides a list of features supported by fabio. \n\n * [Access L"
  },
  {
    "path": "docs/content/feature/access-control.md",
    "chars": 1870,
    "preview": "---\ntitle: \"Access Control\"\nsince: \"1.5.8\"\n---\n\nfabio supports basic ip centric access control per route.  You may\nspeci"
  },
  {
    "path": "docs/content/feature/access-logging.md",
    "chars": 3580,
    "preview": "---\ntitle: \"Access Logging\"\nsince: \"1.4.1\"\n---\n\nSupport for writing access logs for HTTP requests\nin the [Common Log For"
  },
  {
    "path": "docs/content/feature/authorization.md",
    "chars": 2212,
    "preview": "---\ntitle: \"Authorization\"\nsince: \"1.5.11\"\n---\n\nfabio supports basic http authorization on a per-route basis.\n\n<!--more-"
  },
  {
    "path": "docs/content/feature/bgp.md",
    "chars": 3595,
    "preview": "---\ntitle: \"BGP\"\nsince: \"1.6.3\"\n---\n\nNOTE: This feature does not work on Windows at present since the gobgp project\ndoes"
  },
  {
    "path": "docs/content/feature/certificate-stores.md",
    "chars": 9072,
    "preview": "---\ntitle: \"Certificate Stores\"\nsince: \"1.2\"\n---\n\nSupport for dynamic certificate stores which allow you to store certif"
  },
  {
    "path": "docs/content/feature/docker.md",
    "chars": 1659,
    "preview": "---\ntitle: \"Docker Support\"\nsince: \"1.0\"\n---\n\nTo run fabio within Docker use the official Docker image `fabiolb/fabio` a"
  },
  {
    "path": "docs/content/feature/dynamic-reloading.md",
    "chars": 1183,
    "preview": "---\ntitle: \"Dynamic Reloading\"\nsince: \"1.0\"\n---\n\nfabio builds the routing table from the Consul service registrations, h"
  },
  {
    "path": "docs/content/feature/graceful-shutdown.md",
    "chars": 336,
    "preview": "---\ntitle: \"Graceful Shutdown\"\nsince: \"1.0\"\n---\n\nfabio supports a graceful shutdown timeout during which new requests wi"
  },
  {
    "path": "docs/content/feature/grpc-proxy.md",
    "chars": 3176,
    "preview": "---\ntitle: \"GRPC Proxy\"\nsince: \"1.5.11\"\n---\n\nfabio can run a transparent GRPC proxy which dynamically forwards an incomi"
  },
  {
    "path": "docs/content/feature/http-compression.md",
    "chars": 1073,
    "preview": "---\ntitle: \"HTTP Compression\"\nsince: \"1.3.4\"\n---\n\nEnable dynamic compression of responses when the client sets the\n`Acce"
  },
  {
    "path": "docs/content/feature/http-headers.md",
    "chars": 462,
    "preview": "---\ntitle: \"HTTP Header Support\"\nsince: \"1.1.3\"\n---\n\nIn addition, to injecting the `Forwarded` and `X-Real-Ip` headers t"
  },
  {
    "path": "docs/content/feature/http-path-prepending.md",
    "chars": 505,
    "preview": "---\ntitle: \"HTTP Path Prepending\"\nsince: \"1.5.14\"\n---\n\nfabio supports prepending a path to the incoming request. If you "
  },
  {
    "path": "docs/content/feature/http-path-stripping.md",
    "chars": 269,
    "preview": "---\ntitle: \"HTTP Path Stripping\"\nsince: \"1.3.7\"\n---\n\nfabio supports stripping a path from the incoming request. If you w"
  },
  {
    "path": "docs/content/feature/http-redirects.md",
    "chars": 957,
    "preview": "---\ntitle: \"HTTP Redirects\"\nsince: \"1.5.4\"\n---\n\nTo redirect an HTTP request to another URL you can use the `redirect=<co"
  },
  {
    "path": "docs/content/feature/https-tcp-sni-proxy.md",
    "chars": 990,
    "preview": "---\ntitle: \"HTTPS TCP-SNI Proxy\"\nsince: \"1.5.14\"\n---\n\nfabio can run a TCP+SNI routing proxy on a listener, and have fall"
  },
  {
    "path": "docs/content/feature/https-upstream.md",
    "chars": 405,
    "preview": "---\ntitle: \"HTTPS Upstream\"\nsince: \"1.4.2\"\n---\n\nTo support HTTPS upstream servers add the `proto=https` option to the\n`u"
  },
  {
    "path": "docs/content/feature/metrics.md",
    "chars": 3692,
    "preview": "---\ntitle: \"Metrics\"\nsince: \"1.0.0 (Graphite), 1.2.1 (StatsD, DataDog, Circonus), 1.6.0 (Prometheus)\"\n---\n\nFabio collect"
  },
  {
    "path": "docs/content/feature/proxy-protocol.md",
    "chars": 1108,
    "preview": "---\ntitle: \"PROXY Protocol Support\"\nsince: \"1.1.3\"\n---\n\nfabio transparently supports the HA Proxy\n[PROXY protocol](http:"
  },
  {
    "path": "docs/content/feature/sse.md",
    "chars": 352,
    "preview": "---\ntitle: \"Server Sent Events (SSE)\"\nsince: \"1.3\"\n---\n\nfabio detects [SSE](http://www.w3.org/TR/eventsource/) connectio"
  },
  {
    "path": "docs/content/feature/tcp-dynamic-proxy.md",
    "chars": 803,
    "preview": "---\ntitle: \"TCP Dynamic Proxy\"\n---\n\nThe TCP dynamic proxy is similar to the TCP Proxy, but the listener is started from "
  },
  {
    "path": "docs/content/feature/tcp-proxy.md",
    "chars": 614,
    "preview": "---\ntitle: \"TCP Proxy\"\nsince: \"1.4\"\n---\n\nfabio can run a transparent TCP proxy which dynamically forwards an incoming\nco"
  },
  {
    "path": "docs/content/feature/tcp-sni-proxy.md",
    "chars": 1218,
    "preview": "---\ntitle: \"TCP-SNI Proxy\"\nsince: \"1.3\"\n---\n\nfabio can run a transparent TCP proxy with SNI support which can forward an"
  },
  {
    "path": "docs/content/feature/traffic-shaping.md",
    "chars": 1370,
    "preview": "---\ntitle: \"Traffic Shaping\"\nsince: \"1.0\"\n---\n\nfabio allows to control the amount of traffic a set of service instances "
  },
  {
    "path": "docs/content/feature/vault.md",
    "chars": 365,
    "preview": "---\ntitle: \"Vault Support\"\nsince: \"1.2 (Vault KV), 1.5.3 (Vault PKI)\"\n---\n\nfabio can use [Vault](https://vaultproject.io"
  },
  {
    "path": "docs/content/feature/web-ui.md",
    "chars": 332,
    "preview": "---\ntitle: \"Web UI\"\nsincd: \"1.0\"\n---\n\nfabio supports a Web UI to examine the current routing table and manage the\nmanual"
  },
  {
    "path": "docs/content/feature/websockets.md",
    "chars": 1921,
    "preview": "---\ntitle: \"Websockets\"\nsince: \"1.0.5\"\n---\n\nfabio transparently supports Websocket connections by detecting the `Upgrade"
  },
  {
    "path": "docs/content/quickstart/_index.md",
    "chars": 3248,
    "preview": "---\ntitle: \"Quickstart\"\nweight: 100\n---\n\n\n1. Install from source, binary, Docker or Homebrew.\n\n\t```\n\tgo get github.com/f"
  },
  {
    "path": "docs/content/ref/_index.md",
    "chars": 553,
    "preview": "---\ntitle: \"Reference\"\nweight: 600\n---\n\nAll configuration options can be specified either \n\n* in the config file\n* as en"
  },
  {
    "path": "docs/content/ref/bgp.anycastaddresses.md",
    "chars": 376,
    "preview": "---\ntitle: \"bgp.anycastaddresses\"\n---\n\n`bgp.anycastaddresses` sets the anycast addresses we will advertise, \nseparated b"
  },
  {
    "path": "docs/content/ref/bgp.asn.md",
    "chars": 106,
    "preview": "---\ntitle: \"bgp.asn\"\n---\n\n`bgp.asn` sets the asn ID of our router\n\nThe default value is\n\n\tbgp.asn = 65000\n"
  },
  {
    "path": "docs/content/ref/bgp.certfile.md",
    "chars": 171,
    "preview": "---\ntitle: \"bgp.certfile\"\n---\n\n`bgp.certfile` is the file path of the certificate, and is required if bgp.grpctls is set"
  },
  {
    "path": "docs/content/ref/bgp.enabled.md",
    "chars": 123,
    "preview": "---\ntitle: \"bgp.enabled\"\n---\n\n`bgp.enabled` enables the embedded gobgpd daemon\n\nThe default value is\n\n\tbgp.enabled = fal"
  },
  {
    "path": "docs/content/ref/bgp.enablegrpc.md",
    "chars": 183,
    "preview": "---\ntitle: \"bgp.enablegrpc\"\n---\n\n`bgp.enablegrpc` enables the gobgp grpc interface.  \nTo be used with the gobgp command "
  },
  {
    "path": "docs/content/ref/bgp.gobgpdcfgfile.md",
    "chars": 606,
    "preview": "---\ntitle: \"bgp.gobgpdcfgfile\"\n---\n\n`bgp.gobgpdcfgfile` is the optional file path to a \ngobgpd [config file](https://git"
  },
  {
    "path": "docs/content/ref/bgp.grpclistenaddress.md",
    "chars": 198,
    "preview": "---\ntitle: \"bgp.grpclistenaddress\"\n---\n\n`bgp.grpclistenaddress` is the listen interface and port if bgp.enablegrpc is se"
  },
  {
    "path": "docs/content/ref/bgp.grpctls.md",
    "chars": 142,
    "preview": "---\ntitle: \"bgp.grpctls\"\n---\n\n`bgp.grpctls` is whether to enable TLS on the bgp grpc interface.\n\n\nThe default value is\n\n"
  },
  {
    "path": "docs/content/ref/bgp.keyfile.md",
    "chars": 165,
    "preview": "---\ntitle: \"bgp.keyfile\"\n---\n\n`bgp.keyfile` is the file path of the key file, and is required if bgp.grpctls is set to t"
  },
  {
    "path": "docs/content/ref/bgp.listenaddresses.md",
    "chars": 203,
    "preview": "---\ntitle: \"bgp.listenaddresses\"\n---\n\n`bgp.listenaddresses` sets the listen addresses for bgp, separated by comma.\n\nThe "
  },
  {
    "path": "docs/content/ref/bgp.listenport.md",
    "chars": 189,
    "preview": "---\ntitle: \"bgp.listenport\"\n---\n\n`bgp.listenport` sets the listen ports for bgp communication from other routers.\n\nThe d"
  },
  {
    "path": "docs/content/ref/bgp.nexthop.md",
    "chars": 180,
    "preview": "---\ntitle: \"bgp.nexthop\"\n---\n\n`bgp.nexthop` sets the next hop address.  \nIf not set, it uses the [bgp.routerid](/ref/bgp"
  },
  {
    "path": "docs/content/ref/bgp.peers.md",
    "chars": 621,
    "preview": "---\ntitle: \"bgp.peers\"\n---\n\n`bgp.peers` sets the bgp peers we will advertise routes to.  This is required if bgp is enab"
  },
  {
    "path": "docs/content/ref/bgp.routerid.md",
    "chars": 334,
    "preview": "---\ntitle: \"bgp.routerid\"\n---\n\n`bgp.routerid` is the router id (ip address) of this router.  \nThis is required if bgp is"
  },
  {
    "path": "docs/content/ref/glob.cache.size.md",
    "chars": 153,
    "preview": "---\ntitle: \"glob.cache.size\"\n---\n\n`glob.cache.size` Sets the globCache size used for matching on route lookups.\n\nThe def"
  },
  {
    "path": "docs/content/ref/glob.matching.disabled.md",
    "chars": 191,
    "preview": "---\ntitle: \"glob.matching.disabled\"\n---\n\n`glob.matching.disabled` disables glob matching on route lookups.\n\nValid option"
  },
  {
    "path": "docs/content/ref/log.access.format.md",
    "chars": 2593,
    "preview": "---\ntitle: \"log.access.format\"\n---\n\n`log.access.format` configures the format of the access log.\n\nIf the value is either"
  },
  {
    "path": "docs/content/ref/log.access.target.md",
    "chars": 212,
    "preview": "---\ntitle: \"log.access.target\"\n---\n\n`log.access.target` configures where the access log is written to.\n\nOptions are `std"
  },
  {
    "path": "docs/content/ref/log.level.md",
    "chars": 174,
    "preview": "---\ntitle: \"log.level\"\n---\n\n`log.level` configures the log level.\n\nValid levels are `TRACE`, `DEBUG`, `INFO`, `WARN`, `E"
  },
  {
    "path": "docs/content/ref/log.routes.format.md",
    "chars": 430,
    "preview": "---\ntitle: \"log.routes.format\"\n---\n\n`log.routes.format` configures the log output format of routing table updates.\n\nChan"
  },
  {
    "path": "docs/content/ref/metrics.circonus.apiapp.md",
    "chars": 318,
    "preview": "---\ntitle: \"metrics.circonus.apiapp\"\n---\n\n`metrics.circonus.apiapp` configures the API token app to use when\nsubmitting "
  },
  {
    "path": "docs/content/ref/metrics.circonus.apikey.md",
    "chars": 400,
    "preview": "---\ntitle: \"metrics.circonus.apikey\"\n---\n\n`metrics.circonus.apikey` configures the API token key to use when\nsubmitting "
  },
  {
    "path": "docs/content/ref/metrics.circonus.apiurl.md",
    "chars": 336,
    "preview": "---\ntitle: \"metrics.circonus.apiurl\"\n---\n\n`metrics.circonus.apiurl` configures the API URL to use when\nsubmitting metric"
  },
  {
    "path": "docs/content/ref/metrics.circonus.brokerid.md",
    "chars": 400,
    "preview": "---\ntitle: \"metrics.circonus.brokerid\"\n---\n\n`metrics.circonus.brokerid` configures a specific broker to use when\ncreatin"
  },
  {
    "path": "docs/content/ref/metrics.circonus.checkid.md",
    "chars": 392,
    "preview": "---\ntitle: \"metrics.circonus.checkid\"\n---\n\n`metrics.circonus.checkid` configures a specific check to use when\nsubmitting"
  },
  {
    "path": "docs/content/ref/metrics.circonus.submissionurl.md",
    "chars": 449,
    "preview": "---\ntitle: \"metrics.circonus.submissionurl\"\n---\n\n`metrics.circonus.submissionurl` configures a specific check submission"
  },
  {
    "path": "docs/content/ref/metrics.dogstatsd.addr.md",
    "chars": 245,
    "preview": "---\ntitle: \"metrics.dogstatsd.addr\"\n---\n\n`metrics.dogstatsd.addr` configures the host:port of the dogstatsd\nserver. \n\nTh"
  },
  {
    "path": "docs/content/ref/metrics.graphite.addr.md",
    "chars": 241,
    "preview": "---\ntitle: \"metrics.graphite.addr\"\n---\n\n`metrics.graphite.addr` configures the `host:port` of the Graphite server.\n\nThis"
  },
  {
    "path": "docs/content/ref/metrics.interval.md",
    "chars": 150,
    "preview": "---\ntitle: \"metrics.interval\"\n---\n\n`metrics.interval` configures the interval in which metrics are reported.\n\nThe defaul"
  },
  {
    "path": "docs/content/ref/metrics.names.md",
    "chars": 1081,
    "preview": "---\ntitle: \"metrics.names\"\n---\n\n`metrics.names` configures the template for the route metric names\non backends that don'"
  },
  {
    "path": "docs/content/ref/metrics.prefix.md",
    "chars": 925,
    "preview": "---\ntitle: \"metrics.prefix\"\n---\n\n`metrics.prefix` configures the template for the prefix of all reported metrics.\n\nEach "
  },
  {
    "path": "docs/content/ref/metrics.prometheus.buckets.md",
    "chars": 336,
    "preview": "---\ntitle: \"metrics.prometheus.buckets\"\n---\n\n`metrics.prometheus.buckets` configures the time buckets for use with histo"
  },
  {
    "path": "docs/content/ref/metrics.prometheus.path.md",
    "chars": 207,
    "preview": "---\ntitle: \"metrics.prometheus.path\"\n---\n\n`metrics.prometheus.path` configures the path to serve up metrics on any confi"
  },
  {
    "path": "docs/content/ref/metrics.prometheus.subsystem.md",
    "chars": 284,
    "preview": "---\ntitle: \"metrics.prometheus.subsystem\"\n---\n\n`metrics.prometheus.subsystem` configures the subsystem name when reporti"
  },
  {
    "path": "docs/content/ref/metrics.retry.md",
    "chars": 185,
    "preview": "---\ntitle: \"metrics.retry\"\n---\n\n`metrics.retry` configures the interval with which fabio tries to\nconnect to the metrics"
  },
  {
    "path": "docs/content/ref/metrics.statsd.addr.md",
    "chars": 234,
    "preview": "---\ntitle: \"metrics.statsd.addr\"\n---\n\n`metrics.statsd.addr` configures the host:port of the StatsD\nserver. \n\nThis is req"
  },
  {
    "path": "docs/content/ref/metrics.target.md",
    "chars": 1204,
    "preview": "---\ntitle: \"metrics.target\"\n---\n\n`metrics.target` configures the backend the metrics values are sent to.\n\nPossible value"
  },
  {
    "path": "docs/content/ref/metrics.timeout.md",
    "chars": 174,
    "preview": "---\ntitle: \"metrics.timeout\"\n---\n\n`metrics.timeout` configures how long fabio tries to connect to the metrics\nbackend du"
  },
  {
    "path": "docs/content/ref/proxy.addr.md",
    "chars": 4505,
    "preview": "---\ntitle: \"proxy.addr\"\n---\n\n\n`proxy.addr` configures listeners.\n\nEach listener is configured with and address and a\nlis"
  },
  {
    "path": "docs/content/ref/proxy.auth.md",
    "chars": 1709,
    "preview": "---\ntitle: \"proxy.auth\"\n---\n\n`proxy.auth` configures one or more authorization schemes.\n\nEach authorization scheme is co"
  },
  {
    "path": "docs/content/ref/proxy.cs.md",
    "chars": 7081,
    "preview": "---\ntitle: \"proxy.cs\"\n---\n\n`proxy.cs` configures one or more certificate sources.\n\nEach certificate source is configured"
  },
  {
    "path": "docs/content/ref/proxy.deregistergraceperiod.md",
    "chars": 457,
    "preview": "---\ntitle: \"proxy.deregistergraceperiod\"\n---\n\n`proxy.deregistergraceperiod` configures the time to wait before \nshutting"
  },
  {
    "path": "docs/content/ref/proxy.dialtimeout.md",
    "chars": 285,
    "preview": "---\ntitle: \"proxy.dialtimeout\"\n---\n\n`proxy.dialtimeout` configures the connection timeout for\noutgoing connections by se"
  },
  {
    "path": "docs/content/ref/proxy.flushinterval.md",
    "chars": 267,
    "preview": "---\ntitle: \"proxy.flushinterval\"\n---\n\n`proxy.flushinterval` configures periodic flushing of the\nresponse buffer for SSE "
  },
  {
    "path": "docs/content/ref/proxy.globalflushinterval.md",
    "chars": 235,
    "preview": "---\ntitle: \"proxy.globalflushinterval\"\n---\n\n`proxy.globalflushinterval` configures periodic flushing of the\nresponse buf"
  },
  {
    "path": "docs/content/ref/proxy.grpcmaxrxmsgsize.md",
    "chars": 195,
    "preview": "---\ntitle: \"proxy.grpcmaxrxmsgsize\"\n---\n\n`proxy.grpcmaxrxmsgsize` configures the grpc max receive message size in bytes."
  },
  {
    "path": "docs/content/ref/proxy.grpcmaxtxmsgsize.md",
    "chars": 196,
    "preview": "---\ntitle: \"proxy.grpcmaxtxmsgsize\"\n---\n\n`proxy.grpcmaxtxmsgsize` configures the grpc max transmit message size in bytes"
  },
  {
    "path": "docs/content/ref/proxy.grpcshutdowntimeout.md",
    "chars": 291,
    "preview": "---\ntitle: \"proxy.grpcshutdowntimeout\"\n---\n\n`proxy.grpcshutdowntimeout` configures the amount of time fabio will wait to"
  },
  {
    "path": "docs/content/ref/proxy.gzip.contenttype.md",
    "chars": 753,
    "preview": "---\ntitle: \"proxy.gzip.contenttype\"\n---\n\n`proxy.gzip.contenttype` configures which responses should be compressed.\n\nBy d"
  },
  {
    "path": "docs/content/ref/proxy.header.clientip.md",
    "chars": 269,
    "preview": "---\ntitle: \"proxy.header.clientip\"\n---\n\n`proxy.header.clientip` configures the header for the request ip.\n\nThe remote ip"
  },
  {
    "path": "docs/content/ref/proxy.header.requestid.md",
    "chars": 270,
    "preview": "---\ntitle: \"proxy.header.requestid\"\n---\n\n`proxy.header.requestid` configures the header for the adding a unique request "
  },
  {
    "path": "docs/content/ref/proxy.header.sts.maxage.md",
    "chars": 297,
    "preview": "---\ntitle: \"proxy.header.sts.maxage\"\n---\n\n`proxy.header.sts.maxage` enables and configures the max-age of HSTS for TLS r"
  },
  {
    "path": "docs/content/ref/proxy.header.sts.preload.md",
    "chars": 597,
    "preview": "---\ntitle: \"proxy.header.sts.preload\"\n---\n\n`proxy.header.sts.preload` instructs HSTS to include the preload directive.\nW"
  },
  {
    "path": "docs/content/ref/proxy.header.sts.subdomains.md",
    "chars": 275,
    "preview": "---\ntitle: \"proxy.header.sts.subdomains\"\n---\n\n`proxy.header.sts.subdomains` instructs HSTS to include subdomains.\nWhen s"
  },
  {
    "path": "docs/content/ref/proxy.header.tls.md",
    "chars": 298,
    "preview": "---\ntitle: \"proxy.header.tls\"\n---\n\n`proxy.header.tls` configures the header to set for TLS connections.\n\nWhen set to a n"
  },
  {
    "path": "docs/content/ref/proxy.header.tls.value.md",
    "chars": 218,
    "preview": "---\ntitle: \"proxy.header.tls.value\"\n---\n\n`proxy.header.tls.value` configures the value to set the [proxy.header.tls](/re"
  },
  {
    "path": "docs/content/ref/proxy.idleconntimeout.md",
    "chars": 168,
    "preview": "`proxy.idleconntimeout` configures idle connection timeout, which influences\nwhen to close keep-alive connections.\n\nThe "
  },
  {
    "path": "docs/content/ref/proxy.keepalivetimeout.md",
    "chars": 174,
    "preview": "`proxy.keepalivetimeout` configures the keep-alive timeout.\n\nThis configures the KeepAliveTimeout of the network dialer."
  },
  {
    "path": "docs/content/ref/proxy.localip.md",
    "chars": 363,
    "preview": "---\ntitle: \"proxy.localip\"\n---\n\n`proxy.localip` configures the ip address of the proxy which is added\nto the Header conf"
  },
  {
    "path": "docs/content/ref/proxy.matcher.md",
    "chars": 730,
    "preview": "---\ntitle: \"proxy.matcher\"\n---\n\n\n`proxy.matcher` configures the path matching algorithm.\n\n* `prefix`: prefix matching\n* "
  },
  {
    "path": "docs/content/ref/proxy.maxconn.md",
    "chars": 343,
    "preview": "---\ntitle: \"proxy.maxconn\"\n---\n\n`proxy.maxconn` configures the maximum number of cached\nincoming and outgoing connection"
  },
  {
    "path": "docs/content/ref/proxy.noroutestatus.md",
    "chars": 161,
    "preview": "---\ntitle: \"proxy.noroutestatus\"\n---\n\n`proxy.noroutestatus` configures the response code when no route was found.\n\nThe d"
  },
  {
    "path": "docs/content/ref/proxy.responseheadertimeout.md",
    "chars": 305,
    "preview": "---\ntitle: \"proxy.responseheadertimeout\"\n---\n\n`proxy.responseheadertimeout` configures the [ResponseHeaderTimeout](https"
  },
  {
    "path": "docs/content/ref/proxy.shutdownwait.md",
    "chars": 308,
    "preview": "---\ntitle: \"proxy.shutdownwait\"\n---\n\n`proxy.shutdownwait` configures the time for a graceful shutdown.\n\nAfter a signal i"
  },
  {
    "path": "docs/content/ref/proxy.strategy.md",
    "chars": 352,
    "preview": "---\ntitle: \"proxy.strategy\"\n---\n\n`proxy.strategy` configures the load balancing strategy.\n\n* `rnd`: pseudo-random distri"
  },
  {
    "path": "docs/content/ref/registry.backend.md",
    "chars": 1300,
    "preview": "---\ntitle: \"registry.backend\"\n---\n\n`registry.backend` configures which backend is used.\nSupported backends are: `consul`"
  },
  {
    "path": "docs/content/ref/registry.consul.addr.md",
    "chars": 176,
    "preview": "---\ntitle: \"registry.consul.addr\"\n---\n\n`registry.consul.addr` configures the address of the Consul agent to connect to.\n"
  },
  {
    "path": "docs/content/ref/registry.consul.checksRequired.md",
    "chars": 339,
    "preview": "---\ntitle: \"registry.consul.checksRequired\"\n---\n\n`registry.consul.checksRequired` configures how many health checks \nmus"
  },
  {
    "path": "docs/content/ref/registry.consul.kvpath.md",
    "chars": 1035,
    "preview": "---\ntitle: \"registry.consul.kvpath\"\n---\n\n`registry.consul.kvpath` configures the KV path for manual routes.\n\nThe Consul "
  },
  {
    "path": "docs/content/ref/registry.consul.namespace.md",
    "chars": 460,
    "preview": "---\ntitle: \"registry.consul.namespace\"\n---\n\n`registry.consul.namespace` configures the consul namespace in which fabio w"
  },
  {
    "path": "docs/content/ref/registry.consul.noroutehtmlpath.md",
    "chars": 266,
    "preview": "---\ntitle: \"registry.consul.noroutehtmlpath\"\n---\n\n`registry.consul.noroutehtmlpath` configures the KV path for the HTML "
  },
  {
    "path": "docs/content/ref/registry.consul.pollInterval.md",
    "chars": 335,
    "preview": "---\ntitle: \"registry.consul.pollInterval\"\n---\n\n\t\n`registry.consul.pollInterval` configures the poll interval\nfor route u"
  },
  {
    "path": "docs/content/ref/registry.consul.register.addr.md",
    "chars": 357,
    "preview": "---\ntitle: \"registry.consul.register.addr\"\n---\n\n`registry.consul.register.addr` configures the address for the service r"
  }
]

// ... and 148 more files (download for full content)

About this extraction

This page contains the full source code of the fabiolb/fabio GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 348 files (1.2 MB), approximately 484.6k tokens, and a symbol index with 892 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!