mirror of
https://github.com/willnorris/imageproxy.git
synced 2026-04-24 20:36:24 +02:00
Compare commits
98 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf5d6b1d97 | ||
|
|
f9f9a49f8e | ||
|
|
41ff00382c | ||
|
|
64a04fceb8 | ||
|
|
ed6ac75068 | ||
|
|
080f5d05f1 | ||
|
|
69ec880864 | ||
|
|
db2244a25c | ||
|
|
e4f7eada71 | ||
|
|
7f3639886b | ||
|
|
554bfc5d8e | ||
|
|
61346b9f4a | ||
|
|
ba76dfa3b2 | ||
|
|
731fa16921 | ||
|
|
816f1a6855 | ||
|
|
9a1158a703 | ||
|
|
fe35d19c3e | ||
|
|
b98b3455a1 | ||
|
|
df0b6d337a | ||
|
|
2254a1f2ff | ||
|
|
d04e37fc14 | ||
|
|
80bc48388b | ||
|
|
ddf11c9817 | ||
|
|
1ceba2538c | ||
|
|
8170536e41 | ||
|
|
7502adde1c | ||
|
|
82ce506905 | ||
|
|
0da52d1e25 | ||
|
|
f2bc67185d | ||
|
|
07c1c27092 | ||
|
|
707b5ac551 | ||
|
|
73ff297c47 | ||
|
|
5a27a2ec45 | ||
|
|
5b619de74f | ||
|
|
b4c03c294c | ||
|
|
c361000ff4 | ||
|
|
572ad2db78 | ||
|
|
9a0fc21f0c | ||
|
|
46539483be | ||
|
|
3aec0fce3a | ||
|
|
2451f30f07 | ||
|
|
ef50c1f9a6 | ||
|
|
da55ecefd3 | ||
|
|
9708b430f5 | ||
|
|
84960fc8a0 | ||
|
|
a9b6594b41 | ||
|
|
1ba0bd0a6d | ||
|
|
67f506bc9b | ||
|
|
2008a17f5e | ||
|
|
4229b98cb3 | ||
|
|
5afd9081d8 | ||
|
|
eb9dd53848 | ||
|
|
7295bec995 | ||
|
|
f9fd6c9885 | ||
|
|
5bef5a840a | ||
|
|
b37663f12c | ||
|
|
3e9a5ddc3e | ||
|
|
b84da53898 | ||
|
|
3815eb832b | ||
|
|
f314cbfc87 | ||
|
|
431c70aaa9 | ||
|
|
5ffd8db241 | ||
|
|
12d8f92d33 | ||
|
|
3b7abaf462 | ||
|
|
d95da8f749 | ||
|
|
99c0bba938 | ||
|
|
a0eb26e18c | ||
|
|
8c1d05fc2f | ||
|
|
93dcedd516 | ||
|
|
631d363749 | ||
|
|
4121c064ee | ||
|
|
eba736ded3 | ||
|
|
26b706015a | ||
|
|
635cc9ef3b | ||
|
|
a6f541de1b | ||
|
|
b6e5e700cf | ||
|
|
43b75b0ba2 | ||
|
|
0e35e587f2 | ||
|
|
59aad26e29 | ||
|
|
e4a58965b2 | ||
|
|
a1ea6d81a7 | ||
|
|
e35f8298bd | ||
|
|
6022f6a372 | ||
|
|
7584ca6a7b | ||
|
|
a1741ac0e2 | ||
|
|
a81add5d96 | ||
|
|
ef8aec77a9 | ||
|
|
13bafdbf9e | ||
|
|
8fd838a5cc | ||
|
|
e155a719ee | ||
|
|
7c14835b43 | ||
|
|
a4d659dafd | ||
|
|
0f2deb14d2 | ||
|
|
d94e5610d6 | ||
|
|
006b99f6a2 | ||
|
|
49c4bb8ac5 | ||
|
|
77e26e44f5 | ||
|
|
2174b956f3 |
34 changed files with 2817 additions and 1461 deletions
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
|
|
@ -1,9 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
open-pull-requests-limit: 10
|
||||
assignees:
|
||||
- willnorris
|
||||
62
.github/workflows/codeql-analysis.yml
vendored
62
.github/workflows/codeql-analysis.yml
vendored
|
|
@ -2,65 +2,37 @@ name: "CodeQL"
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
branches: [main]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: '0 1 * * 6'
|
||||
- cron: "0 1 * * 6" # run weekly on Saturdays
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Override automatic language detection by changing the below list
|
||||
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
||||
language: ['go']
|
||||
# Learn more...
|
||||
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
|
||||
language: ["go"]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
|
|
|||
71
.github/workflows/docker.yml
vendored
71
.github/workflows/docker.yml
vendored
|
|
@ -1,11 +1,13 @@
|
|||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
name: Publish Docker image
|
||||
branches: ["main"]
|
||||
tags: ["v*"]
|
||||
pull_request:
|
||||
# Run the workflow on pull_request events to ensure we can still build the image.
|
||||
# We only publish the image on push events (see if statements in steps below).
|
||||
branches: ["main"]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
|
|
@ -17,24 +19,49 @@ jobs:
|
|||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 # v1.10.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Setup Docker buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
id: meta
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
if: github.event_name == 'push'
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
# Sign the Docker image
|
||||
- name: Install cosign
|
||||
if: github.event_name == 'push'
|
||||
uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb #v3.8.2
|
||||
- name: Sign the published Docker image
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
COSIGN_YES: "true"
|
||||
run: cosign sign ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}
|
||||
|
|
|
|||
21
.github/workflows/linter.yml
vendored
21
.github/workflows/linter.yml
vendored
|
|
@ -4,21 +4,20 @@ on:
|
|||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
- "**"
|
||||
name: linter
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.x]
|
||||
platform: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: v1.31
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 #v9.2.0
|
||||
with:
|
||||
version: v2.11.4
|
||||
|
|
|
|||
104
.github/workflows/tests.yml
vendored
104
.github/workflows/tests.yml
vendored
|
|
@ -1,59 +1,99 @@
|
|||
name: tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
name: tests
|
||||
env:
|
||||
GO111MODULE: on
|
||||
|
||||
- "**"
|
||||
schedule: # daily at 07:30 UTC
|
||||
- cron: "30 7 * * *"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
go-version:
|
||||
# support the two most recent major go versions
|
||||
- 1.x
|
||||
- 1.16.x
|
||||
go:
|
||||
# test with the two most recent major go versions,
|
||||
# as well as the minimum supported from go.mod.
|
||||
- { go-version: stable }
|
||||
- { go-version: oldstable }
|
||||
- { go-version-file: go.mod }
|
||||
platform: [ubuntu-latest]
|
||||
include:
|
||||
# minimum go version that works. This is not necessarily supported in
|
||||
# any way, and will be bumped up without notice as needed. But it at
|
||||
# least lets us know what go version should work.
|
||||
- go-version: 1.13
|
||||
platform: ubuntu-latest
|
||||
|
||||
# include windows, but only with the latest Go version, since there
|
||||
# is very little in the library that is platform specific
|
||||
- go-version: 1.x
|
||||
- go: { go-version: stable }
|
||||
platform: windows-latest
|
||||
|
||||
# only update test coverage stats with most recent go version on linux
|
||||
- go-version: 1.x
|
||||
- go: { go-version: stable }
|
||||
platform: ubuntu-latest
|
||||
update-coverage: true
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Cache go modules
|
||||
uses: actions/cache@v2
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-go-
|
||||
|
||||
go-version: ${{ matrix.go.go-version }}
|
||||
go-version-file: ${{ matrix.go.go-version-file }}
|
||||
- name: Run go test
|
||||
run: go test -v -race -coverprofile coverage.txt -covermode atomic ./...
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
if: ${{ matrix.update-coverage }}
|
||||
uses: codecov/codecov-action@v1
|
||||
timeout-minutes: 2
|
||||
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
|
||||
test-latest:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
go:
|
||||
- { go-version: stable }
|
||||
- { go-version-file: go.mod }
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ${{ matrix.go.go-version }}
|
||||
go-version-file: ${{ matrix.go.go-version-file }}
|
||||
- uses: geomys/sandboxed-step@7d75eb49d17fdeeb3656b3a57d35932d205bcfb9 # v1.2.1
|
||||
with:
|
||||
run: |
|
||||
go get -u -t ./...
|
||||
go test -race ./...
|
||||
staticcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: stable
|
||||
- uses: geomys/sandboxed-step@7d75eb49d17fdeeb3656b3a57d35932d205bcfb9 # v1.2.1
|
||||
with:
|
||||
run: go run honnef.co/go/tools/cmd/staticcheck@latest ./...
|
||||
govulncheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: stable
|
||||
- uses: geomys/sandboxed-step@7d75eb49d17fdeeb3656b3a57d35932d205bcfb9 # v1.2.1
|
||||
with:
|
||||
run: |
|
||||
go run golang.org/x/vuln/cmd/govulncheck@latest ./...
|
||||
pushd caddy; go run golang.org/x/vuln/cmd/govulncheck@latest ./...; popd
|
||||
|
|
|
|||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.cache
|
||||
./imageproxy
|
||||
|
|
@ -1,24 +1,31 @@
|
|||
version: "2"
|
||||
linters:
|
||||
enable:
|
||||
- dogsled
|
||||
- dupl
|
||||
- goimports
|
||||
- errorlint
|
||||
- gosec
|
||||
- misspell
|
||||
- nakedret
|
||||
- stylecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- whitespace
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
# Some cache implementations use md5 hashes for cached filenames. There is
|
||||
# a slight risk of cache poisoning if an attacker could construct a URL
|
||||
# with the same hash, but it would also need to be allowed by the proxies
|
||||
# security settings. Changing these to a more secure hash algorithm would
|
||||
# result in 100% cache misses when users upgrade. For now, just leave these
|
||||
# alone.
|
||||
- path: internal/.*cache
|
||||
linters: gosec
|
||||
text: G(401|501)
|
||||
# TODO: fix issues and reenable these checks
|
||||
disable:
|
||||
- errcheck
|
||||
- gosec
|
||||
- staticcheck
|
||||
|
||||
exclusions:
|
||||
rules:
|
||||
# Some cache implementations use md5 hashes for cached filenames. There is
|
||||
# a slight risk of cache poisoning if an attacker could construct a URL
|
||||
# with the same hash, but the URL would also need to be allowed by the
|
||||
# proxy's security settings (host allowlist, URL signature, etc). Changing
|
||||
# these to a more secure hash algorithm would result in 100% cache misses
|
||||
# when users upgrade. For now, just leave these alone.
|
||||
- path: internal/.*cache
|
||||
linters:
|
||||
- gosec
|
||||
text: G(401|501)
|
||||
|
|
|
|||
17
Dockerfile
17
Dockerfile
|
|
@ -1,26 +1,23 @@
|
|||
FROM golang:1.17 as build
|
||||
# syntax=docker/dockerfile:1.4
|
||||
FROM --platform=$BUILDPLATFORM cgr.dev/chainguard/wolfi-base as build
|
||||
LABEL maintainer="Will Norris <will@willnorris.com>"
|
||||
|
||||
RUN useradd -u 1001 go
|
||||
RUN apk update && apk add build-base git openssh go-1.24
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -v ./cmd/imageproxy
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -v ./cmd/imageproxy
|
||||
|
||||
FROM scratch
|
||||
FROM cgr.dev/chainguard/static:latest
|
||||
|
||||
COPY --from=build /etc/passwd /etc/passwd
|
||||
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY --from=build /app/imageproxy /app/imageproxy
|
||||
|
||||
USER go
|
||||
|
||||
CMD ["-addr", "0.0.0.0:8080"]
|
||||
ENTRYPOINT ["/app/imageproxy"]
|
||||
|
||||
|
|
|
|||
414
README.md
414
README.md
|
|
@ -5,212 +5,256 @@
|
|||
[](https://codecov.io/gh/willnorris/imageproxy)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/2611)
|
||||
|
||||
imageproxy is a caching image proxy server written in go. It features:
|
||||
imageproxy is a caching image proxy server written in go. It features:
|
||||
|
||||
- basic image adjustments like resizing, cropping, and rotation
|
||||
- access control using allowed hosts list or request signing (HMAC-SHA256)
|
||||
- support for jpeg, png, webp (decode only), tiff, and gif image formats
|
||||
(including animated gifs)
|
||||
- caching in-memory, on disk, or with Amazon S3, Google Cloud Storage, Azure
|
||||
Storage, or Redis
|
||||
- easy deployment, since it's pure go
|
||||
- basic image adjustments like resizing, cropping, and rotation
|
||||
- access control using allowed hosts list or request signing (HMAC-SHA256)
|
||||
- support for jpeg, png, webp (decode only), tiff, and gif image formats
|
||||
(including animated gifs)
|
||||
- caching in-memory, on disk, or with Amazon S3, Google Cloud Storage, Azure
|
||||
Storage, or Redis
|
||||
- easy deployment, since it's pure go
|
||||
|
||||
Personally, I use it primarily to dynamically resize images hosted on my own
|
||||
site (read more in [this post][]). But you can also enable request signing and
|
||||
site (read more in [this post][]). But you can also enable request signing and
|
||||
use it as an SSL proxy for remote images, similar to [atmos/camo][] but with
|
||||
additional image adjustment options.
|
||||
|
||||
I aim to keep imageproxy compatible with the two [most recent major go
|
||||
releases][]. I also keep track of the minimum go version that still works
|
||||
(currently go1.13 with modules enabled), but that might change at any time. You
|
||||
can see the go versions that are tested against in
|
||||
[.github/workflows/tests.yml][].
|
||||
I aim to keep imageproxy compatible with the two [most recent major go releases][].
|
||||
I also keep track of the minimum go version that still works (currently go1.18), but that might change at any time.
|
||||
You can see the go versions that are tested against in [.github/workflows/tests.yml][].
|
||||
|
||||
[this post]: https://willnorris.com/2014/01/a-self-hosted-alternative-to-jetpacks-photon-service
|
||||
[atmos/camo]: https://github.com/atmos/camo
|
||||
[most recent major go releases]: https://golang.org/doc/devel/release.html
|
||||
[.github/workflows/tests.yml]: ./.github/workflows/tests.yml
|
||||
|
||||
## URL Structure ##
|
||||
## URL Structure
|
||||
|
||||
imageproxy URLs are of the form `http://localhost/{options}/{remote_url}`.
|
||||
|
||||
### Options ###
|
||||
### Options
|
||||
|
||||
Options are available for cropping, resizing, rotation, flipping, and digital
|
||||
signatures among a few others. Options for are specified as a comma delimited
|
||||
list of parameters, which can be supplied in any order. Duplicate parameters
|
||||
signatures among a few others. Options for are specified as a comma delimited
|
||||
list of parameters, which can be supplied in any order. Duplicate parameters
|
||||
overwrite previous values.
|
||||
|
||||
See the full list of available options at
|
||||
<https://godoc.org/willnorris.com/go/imageproxy#ParseOptions>.
|
||||
<https://pkg.go.dev/willnorris.com/go/imageproxy#ParseOptions>.
|
||||
|
||||
### Remote URL ###
|
||||
### Remote URL
|
||||
|
||||
The URL of the original image to load is specified as the remainder of the
|
||||
path, without any encoding. For example,
|
||||
`http://localhost/200/https://willnorris.com/logo.jpg`.
|
||||
path. It may be included in plain text without any encoding,
|
||||
percent-encoded (aka URL encoded), or base64 encoded (URL safe, no padding).
|
||||
|
||||
In order to [optimize caching][], it is recommended that URLs not contain query
|
||||
strings.
|
||||
When no encoding is used, any URL query string is treated as part of the remote URL.
|
||||
For example, given the proxy URL of `http://localhost/x/http://example.com/?id=1`,
|
||||
the remote URL is `http://example.com/?id=1`.
|
||||
|
||||
[optimize caching]: http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
|
||||
When percent-encoding is used, the full URL must be encoded.
|
||||
Any query string on the proxy URL is NOT included as part of the remote URL.
|
||||
Percent-encoded URLs must be absolute URLs;
|
||||
they cannot be relative URLs used with a default base URL.
|
||||
For example, `http://localhost/x/http%3A%2F%2Fexample.com%2F%3Fid%3D1`.
|
||||
|
||||
### Examples ###
|
||||
When base64 encoding is used, the full URL must be encoded.
|
||||
Any query string on the proxy URL is NOT included as part of the remote URL.
|
||||
Base64 encoded URLs may be relative URLs used with a default base URL.
|
||||
For example, `http://localhost/x/aHR0cDovL2V4YW1wbGUuY29tLz9pZD0x`.
|
||||
|
||||
### Examples
|
||||
|
||||
The following live examples demonstrate setting different options on [this
|
||||
source image][small-things], which measures 1024 by 678 pixels.
|
||||
|
||||
[small-things]: https://willnorris.com/2013/12/small-things.jpg
|
||||
[small-things]: https://willnorris.com/images/imageproxy/small-things.jpg
|
||||
|
||||
Options | Meaning | Image
|
||||
--------|------------------------------------------|------
|
||||
200x | 200px wide, proportional height | <a href="https://imageproxy.willnorris.com/200x/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/200x/https://willnorris.com/2013/12/small-things.jpg" alt="200x"></a>
|
||||
x0.15 | 15% original height, proportional width | <a href="https://imageproxy.willnorris.com/x0.15/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/x0.15/https://willnorris.com/2013/12/small-things.jpg" alt="x0.15"></a>
|
||||
100x150 | 100 by 150 pixels, cropping as needed | <a href="https://imageproxy.willnorris.com/100x150/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/100x150/https://willnorris.com/2013/12/small-things.jpg" alt="100x150"></a>
|
||||
100 | 100px square, cropping as needed | <a href="https://imageproxy.willnorris.com/100/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/100/https://willnorris.com/2013/12/small-things.jpg" alt="100"></a>
|
||||
150,fit | scale to fit 150px square, no cropping | <a href="https://imageproxy.willnorris.com/150,fit/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/150,fit/https://willnorris.com/2013/12/small-things.jpg" alt="150,fit"></a>
|
||||
100,r90 | 100px square, rotated 90 degrees | <a href="https://imageproxy.willnorris.com/100,r90/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/100,r90/https://willnorris.com/2013/12/small-things.jpg" alt="100,r90"></a>
|
||||
100,fv,fh | 100px square, flipped horizontal and vertical | <a href="https://imageproxy.willnorris.com/100,fv,fh/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/100,fv,fh/https://willnorris.com/2013/12/small-things.jpg" alt="100,fv,fh"></a>
|
||||
200x,q60 | 200px wide, proportional height, 60% quality | <a href="https://imageproxy.willnorris.com/200x,q60/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/200x,q60/https://willnorris.com/2013/12/small-things.jpg" alt="200x,q60"></a>
|
||||
200x,png | 200px wide, converted to PNG format | <a href="https://imageproxy.willnorris.com/200x,png/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/200x,png/https://willnorris.com/2013/12/small-things.jpg" alt="200x,png"></a>
|
||||
cx175,cw400,ch300,100x | crop to 400x300px starting at (175,0), scale to 100px wide | <a href="https://imageproxy.willnorris.com/cx175,cw400,ch300,100x/https://willnorris.com/2013/12/small-things.jpg"><img src="https://imageproxy.willnorris.com/cx175,cw400,ch300,100x/https://willnorris.com/2013/12/small-things.jpg" alt="cx175,cw400,ch300,100x"></a>
|
||||
| Options | Meaning | Image |
|
||||
| ---------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 200x | 200px wide, proportional height | <a href="https://willnorris.com/api/imageproxy/200x/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/200x/https://willnorris.com/images/imageproxy/small-things.jpg" alt="200x"></a> |
|
||||
| x0.15 | 15% original height, proportional width | <a href="https://willnorris.com/api/imageproxy/x0.15/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/x0.15/https://willnorris.com/images/imageproxy/small-things.jpg" alt="x0.15"></a> |
|
||||
| 100x150 | 100 by 150 pixels, cropping as needed | <a href="https://willnorris.com/api/imageproxy/100x150/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/100x150/https://willnorris.com/images/imageproxy/small-things.jpg" alt="100x150"></a> |
|
||||
| 100 | 100px square, cropping as needed | <a href="https://willnorris.com/api/imageproxy/100/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/100/https://willnorris.com/images/imageproxy/small-things.jpg" alt="100"></a> |
|
||||
| 150,fit | scale to fit 150px square, no cropping | <a href="https://willnorris.com/api/imageproxy/150,fit/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/150,fit/https://willnorris.com/images/imageproxy/small-things.jpg" alt="150,fit"></a> |
|
||||
| 100,r90 | 100px square, rotated 90 degrees | <a href="https://willnorris.com/api/imageproxy/100,r90/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/100,r90/https://willnorris.com/images/imageproxy/small-things.jpg" alt="100,r90"></a> |
|
||||
| 100,fv,fh | 100px square, flipped horizontal and vertical | <a href="https://willnorris.com/api/imageproxy/100,fv,fh/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/100,fv,fh/https://willnorris.com/images/imageproxy/small-things.jpg" alt="100,fv,fh"></a> |
|
||||
| 200x,q60 | 200px wide, proportional height, 60% quality | <a href="https://willnorris.com/api/imageproxy/200x,q60/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/200x,q60/https://willnorris.com/images/imageproxy/small-things.jpg" alt="200x,q60"></a> |
|
||||
| 200x,png | 200px wide, converted to PNG format | <a href="https://willnorris.com/api/imageproxy/200x,png/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/200x,png/https://willnorris.com/images/imageproxy/small-things.jpg" alt="200x,png"></a> |
|
||||
| cx175,cw400,ch300,100x | crop to 400x300px starting at (175,0), scale to 100px wide | <a href="https://willnorris.com/api/imageproxy/cx175,cw400,ch300,100x/https://willnorris.com/images/imageproxy/small-things.jpg"><img src="https://willnorris.com/api/imageproxy/cx175,cw400,ch300,100x/https://willnorris.com/images/imageproxy/small-things.jpg" alt="cx175,cw400,ch300,100x"></a> |
|
||||
|
||||
The [smart crop feature](https://godoc.org/willnorris.com/go/imageproxy#hdr-Smart_Crop)
|
||||
The [smart crop feature](https://pkg.go.dev/willnorris.com/go/imageproxy#hdr-Smart_Crop-ParseOptions)
|
||||
can best be seen by comparing crops of [this source image][judah-sheets], with
|
||||
and without smart crop enabled.
|
||||
|
||||
Options | Meaning | Image
|
||||
--------|------------------------------------------|------
|
||||
150x300 | 150x300px, standard crop | <a href="https://imageproxy.willnorris.com/150x300/https://judahnorris.com/images/judah-sheets.jpg"><img src="https://imageproxy.willnorris.com/150x300/https://judahnorris.com/images/judah-sheets.jpg" alt="200x400,sc"></a>
|
||||
150x300,sc | 150x300px, smart crop | <a href="https://imageproxy.willnorris.com/150x300,sc/https://judahnorris.com/images/judah-sheets.jpg"><img src="https://imageproxy.willnorris.com/150x300,sc/https://judahnorris.com/images/judah-sheets.jpg" alt="200x400"></a>
|
||||
| Options | Meaning | Image |
|
||||
| ---------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 150x300 | 150x300px, standard crop | <a href="https://willnorris.com/api/imageproxy/150x300/https://judahnorris.com/images/judah-sheets.jpg"><img src="https://willnorris.com/api/imageproxy/150x300/https://judahnorris.com/images/judah-sheets.jpg" alt="200x400,sc"></a> |
|
||||
| 150x300,sc | 150x300px, smart crop | <a href="https://willnorris.com/api/imageproxy/150x300,sc/https://judahnorris.com/images/judah-sheets.jpg"><img src="https://willnorris.com/api/imageproxy/150x300,sc/https://judahnorris.com/images/judah-sheets.jpg" alt="200x400"></a> |
|
||||
|
||||
[judah-sheets]: https://judahnorris.com/images/judah-sheets.jpg
|
||||
|
||||
Transformation also works on animated gifs. Here is [this source
|
||||
Transformation also works on animated gifs. Here is [this source
|
||||
image][material-animation] resized to 200px square and rotated 270 degrees:
|
||||
|
||||
[material-animation]: https://willnorris.com/2015/05/material-animations.gif
|
||||
[material-animation]: https://willnorris.com/images/imageproxy/material-animations.gif
|
||||
|
||||
<a href="https://imageproxy.willnorris.com/200,r270/https://willnorris.com/2015/05/material-animations.gif"><img src="https://imageproxy.willnorris.com/200,r270/https://willnorris.com/2015/05/material-animations.gif" alt="200,r270"></a>
|
||||
<a href="https://willnorris.com/api/imageproxy/200,r270/https://willnorris.com/images/imageproxy/material-animations.gif"><img src="https://willnorris.com/api/imageproxy/200,r270/https://willnorris.com/images/imageproxy/material-animations.gif" alt="200,r270"></a>
|
||||
|
||||
## Getting Started ##
|
||||
## Getting Started
|
||||
|
||||
Install the package using:
|
||||
|
||||
go install willnorris.com/go/imageproxy/cmd/imageproxy@latest
|
||||
```sh
|
||||
go install willnorris.com/go/imageproxy/cmd/imageproxy@latest
|
||||
```
|
||||
|
||||
Once installed, ensure `$GOPATH/bin` is in your `$PATH`, then run the proxy
|
||||
using:
|
||||
|
||||
imageproxy
|
||||
```sh
|
||||
imageproxy
|
||||
```
|
||||
|
||||
This will start the proxy on port 8080, without any caching and with no allowed
|
||||
host list (meaning any remote URL can be proxied). Test this by navigating to
|
||||
host list (meaning any remote URL can be proxied). Test this by navigating to
|
||||
<http://localhost:8080/500/https://octodex.github.com/images/codercat.jpg> and
|
||||
you should see a 500px square coder octocat.
|
||||
|
||||
### Cache ###
|
||||
### Cache
|
||||
|
||||
By default, the imageproxy command does not cache responses, but caching can be
|
||||
enabled using the `-cache` flag. It supports the following values:
|
||||
enabled using the `-cache` flag. It supports the following values:
|
||||
|
||||
- `memory` - uses an in-memory LRU cache. By default, this is limited to
|
||||
100mb. To customize the size of the cache or the max age for cached items,
|
||||
use the format `memory:size:age` where size is measured in mb and age is a
|
||||
duration. For example, `memory:200:4h` will create a 200mb cache that will
|
||||
cache items no longer than 4 hours.
|
||||
- directory on local disk (e.g. `/tmp/imageproxy`) - will cache images
|
||||
on disk
|
||||
- `memory` - uses an in-memory LRU cache. By default, this is limited to
|
||||
100mb. To customize the size of the cache or the max age for cached items,
|
||||
use the format `memory:size:age` where size is measured in mb and age is a
|
||||
duration. For example, `memory:200:4h` will create a 200mb cache that will
|
||||
cache items no longer than 4 hours.
|
||||
- directory on local disk (e.g. `/tmp/imageproxy`) - will cache images
|
||||
on disk
|
||||
|
||||
- s3 URL (e.g. `s3://region/bucket-name/optional-path-prefix`) - will cache
|
||||
images on Amazon S3. This requires either an IAM role and instance profile
|
||||
with access to your your bucket or `AWS_ACCESS_KEY_ID` and `AWS_SECRET_KEY`
|
||||
environmental variables be set. (Additional methods of loading credentials
|
||||
are documented in the [aws-sdk-go session
|
||||
package](https://docs.aws.amazon.com/sdk-for-go/api/aws/session/)).
|
||||
- s3 URL (e.g. `s3://region/bucket-name/optional-path-prefix`) - will cache
|
||||
images on Amazon S3. This requires either an IAM role and instance profile
|
||||
with access to your your bucket or `AWS_ACCESS_KEY_ID` and `AWS_SECRET_KEY`
|
||||
environmental variables be set. (Additional methods of loading credentials
|
||||
are documented in the [aws-sdk-go session
|
||||
package](https://docs.aws.amazon.com/sdk-for-go/api/aws/session/)).
|
||||
|
||||
Additional configuration options ([further documented here][aws-options])
|
||||
may be specified as URL query string parameters, which are mostly useful
|
||||
when working with s3-compatible services:
|
||||
- "endpoint" - specify an alternate API endpoint
|
||||
- "disableSSL" - set to "1" to disable SSL when calling the API
|
||||
- "s3ForcePathStyle" - set to "1" to force the request to use path-style addressing
|
||||
Additional configuration options ([further documented here][aws-options])
|
||||
may be specified as URL query string parameters, which are mostly useful
|
||||
when working with s3-compatible services:
|
||||
|
||||
For example, when working with [minio](https://minio.io), which doesn't use
|
||||
regions, provide a dummy region value and custom endpoint value:
|
||||
- "endpoint" - specify an alternate API endpoint
|
||||
- "disableSSL" - set to "1" to disable SSL when calling the API
|
||||
- "s3ForcePathStyle" - set to "1" to force the request to use path-style addressing
|
||||
|
||||
s3://fake-region/bucket/folder?endpoint=minio:9000&disableSSL=1&s3ForcePathStyle=1
|
||||
For example, when working with [minio](https://minio.io), which doesn't use
|
||||
regions, provide a dummy region value and custom endpoint value:
|
||||
|
||||
Similarly, for [Digital Ocean Spaces](https://www.digitalocean.com/products/spaces/),
|
||||
provide a dummy region value and the appropriate endpoint for your space:
|
||||
```
|
||||
s3://fake-region/bucket/folder?endpoint=minio:9000&disableSSL=1&s3ForcePathStyle=1
|
||||
```
|
||||
|
||||
s3://fake-region/bucket/folder?endpoint=sfo2.digitaloceanspaces.com
|
||||
Similarly, for [Digital Ocean Spaces](https://www.digitalocean.com/products/spaces/),
|
||||
provide a dummy region value and the appropriate endpoint for your space:
|
||||
|
||||
[aws-options]: https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
|
||||
```
|
||||
s3://fake-region/bucket/folder?endpoint=sfo2.digitaloceanspaces.com
|
||||
```
|
||||
|
||||
- gcs URL (e.g. `gcs://bucket-name/optional-path-prefix`) - will cache images
|
||||
on Google Cloud Storage. Authentication is documented in Google's
|
||||
[Application Default Credentials
|
||||
docs](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application).
|
||||
- azure URL (e.g. `azure://container-name/`) - will cache images on
|
||||
Azure Storage. This requires `AZURESTORAGE_ACCOUNT_NAME` and
|
||||
`AZURESTORAGE_ACCESS_KEY` environment variables to bet set.
|
||||
- redis URL (e.g. `redis://hostname/`) - will cache images on
|
||||
the specified redis host. The full URL syntax is defined by the [redis URI
|
||||
registration](https://www.iana.org/assignments/uri-schemes/prov/redis).
|
||||
Rather than specify password in the URI, use the `REDIS_PASSWORD`
|
||||
environment variable.
|
||||
[aws-options]: https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
|
||||
|
||||
- gcs URL (e.g. `gcs://bucket-name/optional-path-prefix`) - will cache images
|
||||
on Google Cloud Storage. Authentication is documented in Google's
|
||||
[Application Default Credentials
|
||||
docs](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application).
|
||||
- azure URL (e.g. `azure://container-name/`) - will cache images on
|
||||
Azure Storage. This requires `AZURESTORAGE_ACCOUNT_NAME` and
|
||||
`AZURESTORAGE_ACCESS_KEY` environment variables to bet set.
|
||||
- redis URL (e.g. `redis://hostname/`) - will cache images on
|
||||
the specified redis host. The full URL syntax is defined by the [redis URI
|
||||
registration](https://www.iana.org/assignments/uri-schemes/prov/redis).
|
||||
Rather than specify password in the URI, use the `REDIS_PASSWORD`
|
||||
environment variable.
|
||||
|
||||
For example, to cache files on disk in the `/tmp/imageproxy` directory:
|
||||
|
||||
imageproxy -cache /tmp/imageproxy
|
||||
```sh
|
||||
imageproxy -cache /tmp/imageproxy
|
||||
```
|
||||
|
||||
Reload the [codercat URL][], and then inspect the contents of
|
||||
`/tmp/imageproxy`. Within the subdirectories, there should be two files, one
|
||||
`/tmp/imageproxy`. Within the subdirectories, there should be two files, one
|
||||
for the original full-size codercat image, and one for the resized 500px
|
||||
version.
|
||||
|
||||
[codercat URL]: http://localhost:8080/500/https://octodex.github.com/images/codercat.jpg
|
||||
|
||||
Multiple caches can be specified by separating them by spaces or by repeating
|
||||
the `-cache` flag multiple times. The caches will be created in a [tiered
|
||||
the `-cache` flag multiple times. The caches will be created in a [tiered
|
||||
fashion][]. Typically this is used to put a smaller and faster in-memory cache
|
||||
in front of a larger but slower on-disk cache. For example, the following will
|
||||
in front of a larger but slower on-disk cache. For example, the following will
|
||||
first check an in-memory cache for an image, followed by a gcs bucket:
|
||||
|
||||
imageproxy -cache memory -cache gcs://my-bucket/
|
||||
```sh
|
||||
imageproxy -cache memory -cache gcs://my-bucket/
|
||||
```
|
||||
|
||||
[tiered fashion]: https://godoc.org/github.com/die-net/lrucache/twotier
|
||||
[tiered fashion]: https://pkg.go.dev/github.com/die-net/lrucache/twotier
|
||||
|
||||
### Allowed Referrer List ###
|
||||
#### Override Cache Directives
|
||||
|
||||
By default, imageproxy will respect the caching directives in response headers,
|
||||
including the cache duration and explicit instructions **not** to cache the response,
|
||||
such as `no-store` and `private` cache-control directives.
|
||||
|
||||
You can force imageproxy to cache responses, even if they explicitly say not to,
|
||||
by passing the `-forceCache` flag. Note that this is generally not recommended.
|
||||
|
||||
A minimum cache duration can be set using the `-minCacheDuration` flag. This
|
||||
will extend the cache duration if the response header indicates a shorter value.
|
||||
If called without the `-forceCache` flag, this will have no effect on responses
|
||||
with the `no-store` or `private` directives.
|
||||
|
||||
```sh
|
||||
imageproxy -cache /tmp/imageproxy -minCacheDuration 5m
|
||||
```
|
||||
|
||||
### Allowed Referrer List
|
||||
|
||||
You can limit images to only be accessible for certain hosts in the HTTP
|
||||
referrer header, which can help prevent others from hotlinking to images. It can
|
||||
be enabled by running:
|
||||
|
||||
imageproxy -referrers example.com
|
||||
```sh
|
||||
imageproxy -referrers example.com
|
||||
```
|
||||
|
||||
|
||||
Reload the [codercat URL][], and you should now get an error message. You can
|
||||
Reload the [codercat URL][], and you should now get an error message. You can
|
||||
specify multiple hosts as a comma separated list, or prefix a host value with
|
||||
`*.` to allow all sub-domains as well.
|
||||
|
||||
### Allowed and Denied Hosts List ###
|
||||
### Allowed and Denied Hosts List
|
||||
|
||||
You can limit the remote hosts that the proxy will fetch images from using the
|
||||
`allowHosts` and `denyHosts` flags. This is useful, for example, for locking
|
||||
the proxy down to your own hosts to prevent others from abusing it. Of course
|
||||
`allowHosts` and `denyHosts` flags. This is useful, for example, for locking
|
||||
the proxy down to your own hosts to prevent others from abusing it. Of course
|
||||
if you want to support fetching from any host, leave off these flags.
|
||||
|
||||
Try it out by running:
|
||||
|
||||
imageproxy -allowHosts example.com
|
||||
```sh
|
||||
imageproxy -allowHosts example.com
|
||||
```
|
||||
|
||||
Reload the [codercat URL][], and you should now get an error message.
|
||||
Alternately, try running:
|
||||
|
||||
imageproxy -denyHosts octodex.github.com
|
||||
```sh
|
||||
imageproxy -denyHosts octodex.github.com
|
||||
```
|
||||
|
||||
Reloading the [codercat URL][] will still return an error message.
|
||||
|
||||
|
|
@ -221,7 +265,7 @@ blocking reserved ranges like `127.0.0.0/8`, `192.168.0.0/16`, etc.
|
|||
|
||||
If a host matches both an allowed and denied host, the request will be denied.
|
||||
|
||||
### Allowed Content-Type List ###
|
||||
### Allowed Content-Type List
|
||||
|
||||
You can limit what content types can be proxied by using the `contentTypes`
|
||||
flag. By default, this is set to `image/*`, meaning that imageproxy will
|
||||
|
|
@ -229,34 +273,38 @@ process any image types. You can specify multiple content types as a comma
|
|||
separated list, and suffix values with `*` to perform a wildcard match. Set the
|
||||
flag to an empty string to proxy all requests, regardless of content type.
|
||||
|
||||
### Signed Requests ###
|
||||
### Signed Requests
|
||||
|
||||
Instead of an allowed host list, you can require that requests be signed. This
|
||||
Instead of an allowed host list, you can require that requests be signed. This
|
||||
is useful in preventing abuse when you don't have just a static list of hosts
|
||||
you want to allow. Signatures are generated using HMAC-SHA256 against the
|
||||
you want to allow. Signatures are generated using HMAC-SHA256 against the
|
||||
remote URL, and url-safe base64 encoding the result:
|
||||
|
||||
base64urlencode(hmac.New(sha256, <key>).digest(<remote_url>))
|
||||
```
|
||||
base64urlencode(hmac.New(sha256, <key>).digest(<remote_url>))
|
||||
```
|
||||
|
||||
The HMAC key is specified using the `signatureKey` flag. If this flag
|
||||
The HMAC key is specified using the `signatureKey` flag. If this flag
|
||||
begins with an "@", the remainder of the value is interpreted as a file on disk
|
||||
which contains the HMAC key.
|
||||
|
||||
Try it out by running:
|
||||
|
||||
imageproxy -signatureKey "secretkey"
|
||||
```sh
|
||||
imageproxy -signatureKey "secretkey"
|
||||
```
|
||||
|
||||
Reload the [codercat URL][], and you should see an error message. Now load a
|
||||
Reload the [codercat URL][], and you should see an error message. Now load a
|
||||
[signed codercat URL][] (which contains the [signature option][]) and verify
|
||||
that it loads properly.
|
||||
|
||||
[signed codercat URL]: http://localhost:8080/500,sXyMwWKIC5JPCtlYOQ2f4yMBTqpjtUsfI67Sp7huXIYY=/https://octodex.github.com/images/codercat.jpg
|
||||
[signature option]: https://godoc.org/willnorris.com/go/imageproxy#hdr-Signature
|
||||
[signature option]: https://pkg.go.dev/willnorris.com/go/imageproxy#hdr-Signature-ParseOptions
|
||||
|
||||
Some simple code samples for generating signatures in various languages can be
|
||||
found in [docs/url-signing.md](/docs/url-signing.md). Multiple valid signature
|
||||
found in [docs/url-signing.md](/docs/url-signing.md). Multiple valid signature
|
||||
keys may be provided to support key rotation by repeating the `signatureKey`
|
||||
flag multiple times, or by providing a space-separated list of keys. To use a
|
||||
flag multiple times, or by providing a space-separated list of keys. To use a
|
||||
key with a literal space character, load the key from a file using the "@"
|
||||
prefix documented above.
|
||||
|
||||
|
|
@ -264,34 +312,46 @@ If both a whiltelist and signatureKey are specified, requests can match either.
|
|||
In other words, requests that match one of the allowed hosts don't necessarily
|
||||
need to be signed, though they can be.
|
||||
|
||||
### Default Base URL ###
|
||||
To limit how long a URL is valid (particularly useful for signed URLs),
|
||||
you can specify a "valid until" time using the `vu` option with a Unix timestamp.
|
||||
For example, the following signed URL would only be valid until 2020-01-01:
|
||||
|
||||
```
|
||||
http://localhost:8080/vu1577836800,sjNcVf6LxzKEvR6Owgg3zhEMN7xbWxlpf-eyYbRfFK4A=/https://example.com/image
|
||||
```
|
||||
|
||||
### Default Base URL
|
||||
|
||||
Typically, remote images to be proxied are specified as absolute URLs.
|
||||
However, if you commonly proxy images from a single source, you can provide a
|
||||
base URL and then specify remote images relative to that base. Try it out by
|
||||
base URL and then specify remote images relative to that base. Try it out by
|
||||
running:
|
||||
|
||||
imageproxy -baseURL https://octodex.github.com/
|
||||
```sh
|
||||
imageproxy -baseURL https://octodex.github.com/
|
||||
```
|
||||
|
||||
Then load the codercat image, specified as a URL relative to that base:
|
||||
<http://localhost:8080/500/images/codercat.jpg>. Note that this is not an
|
||||
<http://localhost:8080/500/images/codercat.jpg>. Note that this is not an
|
||||
effective method to mask the true source of the images being proxied; it is
|
||||
trivial to discover the base URL being used. Even when a base URL is
|
||||
trivial to discover the base URL being used. Even when a base URL is
|
||||
specified, you can always provide the absolute URL of the image to be proxied.
|
||||
|
||||
### Scaling beyond original size ###
|
||||
### Scaling beyond original size
|
||||
|
||||
By default, the imageproxy won't scale images beyond their original size.
|
||||
However, you can use the `scaleUp` command-line flag to allow this to happen:
|
||||
|
||||
imageproxy -scaleUp true
|
||||
```sh
|
||||
imageproxy -scaleUp true
|
||||
```
|
||||
|
||||
### WebP and TIFF support ###
|
||||
### WebP and TIFF support
|
||||
|
||||
Imageproxy can proxy remote webp images, but they will be served in either jpeg
|
||||
or png format (this is because the golang webp library only supports webp
|
||||
decoding) if any transformation is requested. If no format is specified,
|
||||
imageproxy will use jpeg by default. If no transformation is requested (for
|
||||
decoding) if any transformation is requested. If no format is specified,
|
||||
imageproxy will use jpeg by default. If no transformation is requested (for
|
||||
example, if you are just using imageproxy as an SSL proxy) then the original
|
||||
webp image will be served as-is without any format conversion.
|
||||
|
||||
|
|
@ -300,87 +360,131 @@ default if any transformation is requested. To force encoding as tiff, pass the
|
|||
"tiff" option. Like webp, tiff images will be served as-is without any format
|
||||
conversion if no transformation is requested.
|
||||
|
||||
|
||||
Run `imageproxy -help` for a complete list of flags the command accepts. If
|
||||
Run `imageproxy -help` for a complete list of flags the command accepts. If
|
||||
you want to use a different caching implementation, it's probably easiest to
|
||||
just make a copy of `cmd/imageproxy/main.go` and customize it to fit your
|
||||
needs... it's a very simple command.
|
||||
|
||||
### Environment Variables ###
|
||||
### Environment Variables
|
||||
|
||||
All configuration flags have equivalent environment variables of the form
|
||||
`IMAGEPROXY_$NAME`. For example, an on-disk cache could be configured by calling
|
||||
`IMAGEPROXY_$NAME`. For example, an on-disk cache could be configured by calling
|
||||
|
||||
IMAGEPROXY_CACHE="/tmp/imageproxy" imageproxy
|
||||
```sh
|
||||
IMAGEPROXY_CACHE="/tmp/imageproxy" imageproxy
|
||||
```
|
||||
|
||||
## Deploying ##
|
||||
## Deploying
|
||||
|
||||
In most cases, you can follow the normal procedure for building a deploying any
|
||||
go application. For example:
|
||||
go application. For example:
|
||||
|
||||
- `go build willnorris.com/go/imageproxy/cmd/imageproxy`
|
||||
- copy resulting binary to `/usr/local/bin`
|
||||
- copy [`etc/imageproxy.service`](etc/imageproxy.service) to
|
||||
`/lib/systemd/system` and enable using `systemctl`.
|
||||
- `go build willnorris.com/go/imageproxy/cmd/imageproxy`
|
||||
- copy resulting binary to `/usr/local/bin`
|
||||
- copy [`etc/imageproxy.service`](etc/imageproxy.service) to
|
||||
`/lib/systemd/system` and enable using `systemctl`.
|
||||
|
||||
Instructions have been contributed below for running on other platforms, but I
|
||||
don't have much experience with them personally.
|
||||
|
||||
### Heroku ###
|
||||
### Heroku
|
||||
|
||||
It's easy to vendorize the dependencies with `Godep` and deploy to Heroku. Take
|
||||
a look at [this GitHub repo](https://github.com/oreillymedia/prototype-imageproxy/tree/heroku)
|
||||
(make sure you use the `heroku` branch).
|
||||
|
||||
### AWS Elastic Beanstalk ###
|
||||
### AWS Elastic Beanstalk
|
||||
|
||||
[O’Reilly Media](https://github.com/oreillymedia) set up [a repository](https://github.com/oreillymedia/prototype-imageproxy)
|
||||
with everything you need to deploy imageproxy to Elastic Beanstalk. Just follow the instructions
|
||||
in the [README](https://github.com/oreillymedia/prototype-imageproxy/blob/master/Readme.md).
|
||||
|
||||
### Docker ###
|
||||
### Docker
|
||||
|
||||
A docker image is available at [`ghcr.io/willnorris/imageproxy`](https://github.com/willnorris/imageproxy/pkgs/container/imageproxy).
|
||||
|
||||
You can run it by
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run -p 8080:8080 ghcr.io/willnorris/imageproxy -addr 0.0.0.0:8080
|
||||
```
|
||||
|
||||
Or in your Dockerfile:
|
||||
|
||||
```
|
||||
```Dockerfile
|
||||
ENTRYPOINT ["/app/imageproxy", "-addr 0.0.0.0:8080"]
|
||||
```
|
||||
|
||||
If running imageproxy inside docker with a bind-mounted on-disk cache, make sure
|
||||
the container is running as a user that has write permission to the mounted host
|
||||
directory. See more details in
|
||||
directory. See more details in
|
||||
[#198](https://github.com/willnorris/imageproxy/issues/198).
|
||||
|
||||
### nginx ###
|
||||
Note that all configuration options can be set using [environment
|
||||
variables](#environment-variables), which is often the preferred approach for
|
||||
containers.
|
||||
|
||||
### Caddy
|
||||
|
||||
You can proxy requests to imageproxy in your Caddy config using the `reverse_proxy` directive:
|
||||
|
||||
```Caddyfile
|
||||
@imageproxy path /api/imageproxy/*
|
||||
handle @imageproxy {
|
||||
uri replace /api/imageproxy/ /
|
||||
reverse_proxy http://localhost:4593
|
||||
}
|
||||
```
|
||||
|
||||
You can also run an instance of imageproxy embedded in Caddy using the [caddy module](./caddy/).
|
||||
This requires a custom build of Caddy with the imageproxy module included
|
||||
([example](https://github.com/willnorris/willnorris.com/blob/main/cmd/caddy/caddy.go)),
|
||||
and configuring it with the `imageproxy` directive in your Caddyfile:
|
||||
|
||||
```Caddyfile
|
||||
@imageproxy path /api/imageproxy/*
|
||||
handle @imageproxy {
|
||||
uri replace /api/imageproxy/ /
|
||||
|
||||
imageproxy {
|
||||
cache /data/imageproxy-cache
|
||||
default_base_url {$IMAGEPROXY_BASEURL}
|
||||
allow_hosts {$IMAGEPROXY_ALLOWHOSTS}
|
||||
signature_key {$IMAGEPROXY_SIGNATUREKEY}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### nginx
|
||||
|
||||
Use the `proxy_pass` directive to send requests to your imageproxy instance.
|
||||
For example, to run imageproxy at the path "/api/imageproxy/", set:
|
||||
|
||||
```
|
||||
location /api/imageproxy/ {
|
||||
proxy_pass http://localhost:4593/;
|
||||
}
|
||||
```nginx
|
||||
location /api/imageproxy/ {
|
||||
proxy_pass http://localhost:4593/;
|
||||
}
|
||||
```
|
||||
|
||||
Depending on other directives you may have in your nginx config, you might need
|
||||
to alter the precedence order by setting:
|
||||
|
||||
```
|
||||
location ^~ /api/imageproxy/ {
|
||||
proxy_pass http://localhost:4593/;
|
||||
}
|
||||
```nginx
|
||||
location ^~ /api/imageproxy/ {
|
||||
proxy_pass http://localhost:4593/;
|
||||
}
|
||||
```
|
||||
|
||||
## License ##
|
||||
## Clients
|
||||
|
||||
- [Hugo partial](https://github.com/willnorris/willnorris.com/blob/main/layouts/partials/imageproxy-url.html)
|
||||
(I use this with an [`{{<img>}}` shortcode](https://github.com/willnorris/willnorris.com/blob/main/layouts/shortcodes/img.html)
|
||||
like [this example](https://github.com/willnorris/willnorris.com/blob/b7f3451/content/about/index.md?plain=1#L7))
|
||||
- [Ruby](https://github.com/azolf/imageproxy_ruby)
|
||||
|
||||
## License
|
||||
|
||||
imageproxy is copyright its respective authors. All of my personal work on
|
||||
imageproxy through 2020 (which accounts for the majority of the code) is
|
||||
copyright Google, my employer at the time. It is available under the [Apache
|
||||
copyright Google, my employer at the time. It is available under the [Apache
|
||||
2.0 License](./LICENSE).
|
||||
|
|
|
|||
143
caddy/go.mod
Normal file
143
caddy/go.mod
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
module willnorris.com/go/imageproxy/caddy
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/caddyserver/caddy/v2 v2.11.2
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible
|
||||
go.uber.org/zap v1.27.1
|
||||
willnorris.com/go/imageproxy v0.12.0
|
||||
)
|
||||
|
||||
replace willnorris.com/go/imageproxy => ../
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.25.1 // indirect
|
||||
cloud.google.com/go/auth v0.18.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/bigmod v0.1.0 // indirect
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/KimMachineGun/automemlimit v0.7.5 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/caddyserver/certmagic v0.25.2 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.5 // indirect
|
||||
github.com/ccoveille/go-safecast/v2 v2.0.0 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/dgraph-io/badger v1.6.2 // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.2.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fcjr/aia-transport-go v1.2.2 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/cel-go v0.27.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.18.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.8.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/klauspost/compress v1.18.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/libdns/libdns v1.1.1 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.6 // indirect
|
||||
github.com/miekg/dns v1.1.72 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/muesli/smartcrop v0.3.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.20.1 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/slackhq/nebula v1.10.3 // indirect
|
||||
github.com/smallstep/certificates v0.30.0 // indirect
|
||||
github.com/smallstep/cli-utils v0.12.2 // indirect
|
||||
github.com/smallstep/linkedca v0.25.0 // indirect
|
||||
github.com/smallstep/nosql v0.8.0 // indirect
|
||||
github.com/smallstep/pkcs7 v0.2.1 // indirect
|
||||
github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 // indirect
|
||||
github.com/smallstep/truststore v0.13.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/cobra v1.10.2 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
||||
github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747 // indirect
|
||||
github.com/urfave/cli v1.22.17 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect
|
||||
go.opentelemetry.io/otel v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
||||
go.step.sm/crypto v0.77.1 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20260213171211-a408498e5541 // indirect
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||
golang.org/x/image v0.38.0 // indirect
|
||||
golang.org/x/mod v0.34.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/term v0.41.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
google.golang.org/api v0.271.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
willnorris.com/go/gifresize v1.0.0 // indirect
|
||||
)
|
||||
512
caddy/go.sum
Normal file
512
caddy/go.sum
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
|
||||
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
|
||||
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
||||
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
|
||||
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
|
||||
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
|
||||
cloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU=
|
||||
cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58=
|
||||
cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=
|
||||
cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=
|
||||
code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE=
|
||||
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
filippo.io/bigmod v0.1.0 h1:UNzDk7y9ADKST+axd9skUpBQeW7fG2KrTZyOE4uGQy8=
|
||||
filippo.io/bigmod v0.1.0/go.mod h1:OjOXDNlClLblvXdwgFFOQFJEocLhhtai8vGLy0JCZlI=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/DeRuina/timberjack v1.3.9 h1:6UXZ1I7ExPGTX/1UNYawR58LlOJUHKBPiYC7WQ91eBo=
|
||||
github.com/DeRuina/timberjack v1.3.9/go.mod h1:RLoeQrwrCGIEF8gO5nV5b/gMD0QIy7bzQhBUgpp1EqE=
|
||||
github.com/KimMachineGun/automemlimit v0.7.5 h1:RkbaC0MwhjL1ZuBKunGDjE/ggwAX43DwZrJqVwyveTk=
|
||||
github.com/KimMachineGun/automemlimit v0.7.5/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk=
|
||||
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
|
||||
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/caddyserver/caddy/v2 v2.11.2 h1:iOlpsSiSKqEW+SIXrcZsZ/NO74SzB/ycqqvAIEfIm64=
|
||||
github.com/caddyserver/caddy/v2 v2.11.2/go.mod h1:ASNYYmKhIVWWMGPfNxclI5DqKEgU3FhmL+6NZWzQEag=
|
||||
github.com/caddyserver/certmagic v0.25.2 h1:D7xcS7ggX/WEY54x0czj7ioTkmDWKIgxtIi2OcQclUc=
|
||||
github.com/caddyserver/certmagic v0.25.2/go.mod h1:llW/CvsNmza8S6hmsuggsZeiX+uS27dkqY27wDIuBWg=
|
||||
github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE=
|
||||
github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/ccoveille/go-safecast/v2 v2.0.0 h1:+5eyITXAUj3wMjad6cRVJKGnC7vDS55zk0INzJagub0=
|
||||
github.com/ccoveille/go-safecast/v2 v2.0.0/go.mod h1:JIYA4CAR33blIDuE6fSwCp2sz1oOBahXnvmdBhOAABs=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
||||
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
|
||||
github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/die-net/lrucache v0.0.0-20220628165024-20a71bc65bf1 h1:1nCGINecpltGpOWruhy+Ac2/FRy+p1igMylF+MsijpI=
|
||||
github.com/die-net/lrucache v0.0.0-20220628165024-20a71bc65bf1/go.mod h1:NQKJ1XiOlLRLoAeq/5LE3GBlSukAK3zDUUlrvc2rfCQ=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fcjr/aia-transport-go v1.2.2 h1:sIZqXcM+YhTd2BDtkV2OJaqbcIVcPv1oKru3VJPIPc8=
|
||||
github.com/fcjr/aia-transport-go v1.2.2/go.mod h1:onSqSq3tGkM14WusDx7q9FTheS9R1KBtD+QBWI6zG/w=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
|
||||
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo=
|
||||
github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw=
|
||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
|
||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/google/go-tpm-tools v0.4.7 h1:J3ycC8umYxM9A4eF73EofRZu4BxY0jjQnUnkhIBbvws=
|
||||
github.com/google/go-tpm-tools v0.4.7/go.mod h1:gSyXTZHe3fgbzb6WEGd90QucmsnT1SRdlye82gH8QjQ=
|
||||
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
||||
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
|
||||
github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI=
|
||||
github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
|
||||
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
|
||||
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU=
|
||||
github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ=
|
||||
github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U=
|
||||
github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk=
|
||||
github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc=
|
||||
github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78RwcQLUkocpI=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
|
||||
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
|
||||
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||
github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
|
||||
github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/slackhq/nebula v1.10.3 h1:EstYj8ODEcv6T0R9X5BVq1zgWZnyU5gtPzk99QF1PMU=
|
||||
github.com/slackhq/nebula v1.10.3/go.mod h1:IL5TUQm4x9IFx2kCKPYm1gP47pwd5b8QGnnBH2RHnvs=
|
||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
|
||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
|
||||
github.com/smallstep/certificates v0.30.0 h1:faDmyVxF0SpArbogBHMs1jj4tVKpXS+2ALGsq6pc8KU=
|
||||
github.com/smallstep/certificates v0.30.0/go.mod h1:VuSC7WLIWomjkwJTU7YJX/ZPm4cXuqqnDl4sYEDP9oU=
|
||||
github.com/smallstep/cli-utils v0.12.2 h1:lGzM9PJrH/qawbzMC/s2SvgLdJPKDWKwKzx9doCVO+k=
|
||||
github.com/smallstep/cli-utils v0.12.2/go.mod h1:uCPqefO29goHLGqFnwk0i8W7XJu18X3WHQFRtOm/00Y=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca h1:VX8L0r8vybH0bPeaIxh4NQzafKQiqvlOn8pmOXbFLO4=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
|
||||
github.com/smallstep/linkedca v0.25.0 h1:txT9QHGbCsJq0MhAghBq7qhurGY727tQuqUi+n4BVBo=
|
||||
github.com/smallstep/linkedca v0.25.0/go.mod h1:Q3jVAauFKNlF86W5/RFtgQeyDKz98GL/KN3KG4mJOvc=
|
||||
github.com/smallstep/nosql v0.8.0 h1:FBTCUfKPmWYbrozW+RBKu+fnvbn+zr5rVli/XB4Jp4A=
|
||||
github.com/smallstep/nosql v0.8.0/go.mod h1:5dUpNotHLHhOUapP0PLBVVfp3tG1DFC31VRccg+Cqwo=
|
||||
github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA=
|
||||
github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0=
|
||||
github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 h1:k23+s51sgYix4Zgbvpmy+1ZgXLjr4ZTkBTqXmpnImwA=
|
||||
github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492/go.mod h1:QQhwLqCS13nhv8L5ov7NgusowENUtXdEzdytjmJHdZQ=
|
||||
github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
|
||||
github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
|
||||
github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747 h1:RnBbFMmodYzhC6adOjTbtUQXyzV8dcvKYbolzs6Qch0=
|
||||
github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747/go.mod h1:ejPAJui3kVK4u5TgMtqtXlWf5HnKh9fLy5kvpaeuas0=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
|
||||
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
|
||||
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
|
||||
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.step.sm/crypto v0.77.1 h1:4EEqfKdv0egQ1lqz2RhnU8Jv6QgXZfrgoxWMqJF9aDs=
|
||||
go.step.sm/crypto v0.77.1/go.mod h1:U/SsmEm80mNnfD5WIkbhuW/B1eFp3fgFvdXyDLpU1AQ=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20260213171211-a408498e5541 h1:FmKxj9ocLKn45jiR2jQMwCVhDvaK7fKQFzfuT9GvyK8=
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20260213171211-a408498e5541/go.mod h1:+UoQFNBq2p2wO+Q6ddVtYc25GZ6VNdOMyyrd4nrqrKs=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=
|
||||
golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.271.0 h1:cIPN4qcUc61jlh7oXu6pwOQqbJW2GqYh5PS6rB2C/JY=
|
||||
google.golang.org/api v0.271.0/go.mod h1:CGT29bhwkbF+i11qkRUJb2KMKqcJ1hdFceEIRd9u64Q=
|
||||
google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d h1:vsOm753cOAMkt76efriTCDKjpCbK18XGHMJHo0JUKhc=
|
||||
google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:0oz9d7g9QLSdv9/lgbIjowW1JoxMbxmBVNe8i6tORJI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d h1:EocjzKLywydp5uZ5tJ79iP6Q0UjDnyiHkGRWxuPBP8s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:48U2I+QQUYhsFrg2SY6r+nJzeOtjey7j//WBESw+qyQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
willnorris.com/go/gifresize v1.0.0 h1:GKS68zjNhHMqkgNTv4iFAO/j/sNcVSOHQ7SqmDAIAmM=
|
||||
willnorris.com/go/gifresize v1.0.0/go.mod h1:eBM8gogBGCcaH603vxSpnfjwXIpq6nmnj/jauBDKtAk=
|
||||
166
caddy/module.go
Normal file
166
caddy/module.go
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
// Package caddy provides ImageProxy as a Caddy module.
|
||||
package caddy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
caddy "github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
"github.com/gregjones/httpcache/diskcache"
|
||||
"github.com/peterbourgon/diskv"
|
||||
"go.uber.org/zap"
|
||||
"willnorris.com/go/imageproxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(ImageProxy{})
|
||||
httpcaddyfile.RegisterHandlerDirective("imageproxy", parseCaddyfile)
|
||||
httpcaddyfile.RegisterDirectiveOrder("imageproxy", "after", "reverse_proxy")
|
||||
}
|
||||
|
||||
type ImageProxy struct {
|
||||
Cache string `json:"cache,omitempty"`
|
||||
|
||||
DefaultBaseURL string `json:"default_base_url,omitempty"`
|
||||
|
||||
AllowHosts []string `json:"allow_hosts,omitempty"`
|
||||
DenyHosts []string `json:"deny_hosts,omitempty"`
|
||||
Referrers []string `json:"referrers,omitempty"`
|
||||
ContentTypes []string `json:"content_types,omitempty"`
|
||||
|
||||
SignatureKeys []string `json:"signature_keys,omitempty"`
|
||||
Verbose bool `json:"verbose,omitempty"`
|
||||
|
||||
logger *zap.Logger
|
||||
proxy *imageproxy.Proxy
|
||||
}
|
||||
|
||||
// interface guard
|
||||
var (
|
||||
_ caddyhttp.MiddlewareHandler = (*ImageProxy)(nil)
|
||||
)
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (ImageProxy) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "http.handlers.imageproxy",
|
||||
New: func() caddy.Module { return new(ImageProxy) },
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ImageProxy) Provision(ctx caddy.Context) error {
|
||||
p.logger = ctx.Logger()
|
||||
cache, _ := parseCache(p.Cache)
|
||||
p.proxy = imageproxy.NewProxy(nil, cache)
|
||||
p.proxy.DefaultBaseURL, _ = url.Parse(p.DefaultBaseURL)
|
||||
p.proxy.AllowHosts = p.AllowHosts
|
||||
p.proxy.DenyHosts = p.DenyHosts
|
||||
p.proxy.Referrers = p.Referrers
|
||||
p.proxy.ContentTypes = p.ContentTypes
|
||||
if len(p.proxy.ContentTypes) == 0 {
|
||||
p.proxy.ContentTypes = []string{"image/*"}
|
||||
}
|
||||
for _, key := range p.SignatureKeys {
|
||||
p.proxy.SignatureKeys = append(p.proxy.SignatureKeys, []byte(key))
|
||||
}
|
||||
p.proxy.Logger = zap.NewStdLog(p.logger)
|
||||
p.proxy.Verbose = p.Verbose
|
||||
p.proxy.FollowRedirects = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ImageProxy) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error {
|
||||
p.proxy.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||
p := new(ImageProxy)
|
||||
|
||||
h.Next() // consume the directive name
|
||||
for nesting := h.Nesting(); h.NextBlock(nesting); {
|
||||
switch h.Val() {
|
||||
case "cache":
|
||||
if !h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
p.Cache = h.Val()
|
||||
case "default_base_url":
|
||||
if !h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
p.DefaultBaseURL = h.Val()
|
||||
case "allow_hosts":
|
||||
if !h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
p.AllowHosts = append(p.AllowHosts, strings.Split(h.Val(), ",")...)
|
||||
case "deny_hosts":
|
||||
if !h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
p.DenyHosts = append(p.DenyHosts, strings.Split(h.Val(), ",")...)
|
||||
case "referrers":
|
||||
if !h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
p.Referrers = append(p.Referrers, strings.Split(h.Val(), ",")...)
|
||||
case "content_types":
|
||||
if !h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
p.ContentTypes = append(p.ContentTypes, strings.Split(h.Val(), ",")...)
|
||||
case "signature_key":
|
||||
if !h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
p.SignatureKeys = append(p.SignatureKeys, h.Val())
|
||||
case "verbose":
|
||||
if !h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
p.Verbose, _ = strconv.ParseBool(h.Val())
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// parseCache parses c returns the specified Cache implementation.
|
||||
func parseCache(c string) (imageproxy.Cache, error) {
|
||||
const defaultMemorySize = 100
|
||||
|
||||
if c == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if c == "memory" {
|
||||
c = fmt.Sprintf("memory:%d", defaultMemorySize)
|
||||
}
|
||||
|
||||
u, err := url.Parse(c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing cache flag: %w", err)
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "file":
|
||||
return diskCache(u.Path), nil
|
||||
default:
|
||||
return diskCache(c), nil
|
||||
}
|
||||
}
|
||||
|
||||
func diskCache(path string) *diskcache.Cache {
|
||||
d := diskv.New(diskv.Options{
|
||||
BasePath: path,
|
||||
|
||||
// For file "c0ffee", store file as "c0/ff/c0ffee"
|
||||
Transform: func(s string) []string { return []string{s[0:2], s[2:4]} },
|
||||
})
|
||||
return diskcache.NewWithDiskv(d)
|
||||
}
|
||||
|
|
@ -12,7 +12,6 @@ import (
|
|||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
|
@ -53,7 +52,7 @@ func sign(key string, s string, urlOnly bool) ([]byte, error) {
|
|||
|
||||
k, err := parseKey(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing key: %v", err)
|
||||
return nil, fmt.Errorf("error parsing key: %w", err)
|
||||
}
|
||||
|
||||
mac := hmac.New(sha256.New, k)
|
||||
|
|
@ -65,7 +64,7 @@ func sign(key string, s string, urlOnly bool) ([]byte, error) {
|
|||
|
||||
func parseKey(s string) ([]byte, error) {
|
||||
if strings.HasPrefix(s, "@") {
|
||||
return ioutil.ReadFile(s[1:])
|
||||
return os.ReadFile(s[1:])
|
||||
}
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
|
|
@ -13,30 +12,6 @@ import (
|
|||
|
||||
var key = "secret"
|
||||
|
||||
func TestMainFunc(t *testing.T) {
|
||||
os.Args = []string{"imageproxy-sign", "-key", key, "http://example.com/#0x0"}
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Errorf("error creating pipe: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
os.Stdout = w
|
||||
|
||||
main()
|
||||
w.Close()
|
||||
|
||||
output, err := ioutil.ReadAll(r)
|
||||
got := string(output)
|
||||
if err != nil {
|
||||
t.Errorf("error reading from pipe: %v", err)
|
||||
}
|
||||
|
||||
want := "url: http://example.com/#0x0\nsignature: pwlnJ3bVazxg2nQxClimqT0VnNxUm5W0cdyg1HpKUPY=\n"
|
||||
if got != want {
|
||||
t.Errorf("main output %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
s := "http://example.com/image.jpg#0x0"
|
||||
|
||||
|
|
@ -94,7 +69,7 @@ func TestParseKey(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParseKey_FilePath(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "key")
|
||||
f, err := os.CreateTemp("", "key")
|
||||
if err != nil {
|
||||
t.Errorf("error creating temp file: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
|
@ -20,7 +20,6 @@ import (
|
|||
"github.com/die-net/lrucache"
|
||||
"github.com/die-net/lrucache/twotier"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gregjones/httpcache/diskcache"
|
||||
rediscache "github.com/gregjones/httpcache/redis"
|
||||
"github.com/peterbourgon/diskv"
|
||||
|
|
@ -32,13 +31,15 @@ import (
|
|||
|
||||
const defaultMemorySize = 100
|
||||
|
||||
var addr = flag.String("addr", "localhost:8080", "TCP address to listen on")
|
||||
var addr = flag.String("addr", "localhost:8080", "address to listen on, either a TCP address or a Unix domain socket path prefixed with unix:")
|
||||
var allowHosts = flag.String("allowHosts", "", "comma separated list of allowed remote hosts")
|
||||
var denyHosts = flag.String("denyHosts", "", "comma separated list of denied remote hosts")
|
||||
var referrers = flag.String("referrers", "", "comma separated list of allowed referring hosts")
|
||||
var includeReferer = flag.Bool("includeReferer", false, "include referer header in remote requests")
|
||||
var followRedirects = flag.Bool("followRedirects", true, "follow redirects")
|
||||
var baseURL = flag.String("baseURL", "", "default base URL for relative remote URLs")
|
||||
var passRequestHeaders = flag.String("passRequestHeaders", "", "comma separatetd list of request headers to pass to remote server")
|
||||
var passResponseHeaders = flag.String("passResponseHeaders", "Cache-Control,Last-Modified,Expires,Etag,Link", "comma separated list of response headers to pass from remote server")
|
||||
var cache tieredCache
|
||||
var signatureKeys signatureKeyList
|
||||
var scaleUp = flag.Bool("scaleUp", false, "allow images to scale beyond their original dimensions")
|
||||
|
|
@ -47,6 +48,8 @@ var verbose = flag.Bool("verbose", false, "print verbose logging messages")
|
|||
var _ = flag.Bool("version", false, "Deprecated: this flag does nothing")
|
||||
var contentTypes = flag.String("contentTypes", "image/*", "comma separated list of allowed content types")
|
||||
var userAgent = flag.String("userAgent", "willnorris/imageproxy", "specify the user-agent used by imageproxy when fetching images from origin website")
|
||||
var minCacheDuration = flag.Duration("minCacheDuration", 0, "minimum duration to cache remote images")
|
||||
var forceCache = flag.Bool("forceCache", false, "Ignore no-store and private directives in responses")
|
||||
|
||||
func init() {
|
||||
flag.Var(&cache, "cache", "location to cache images (see https://github.com/willnorris/imageproxy#cache)")
|
||||
|
|
@ -70,6 +73,15 @@ func main() {
|
|||
if *contentTypes != "" {
|
||||
p.ContentTypes = strings.Split(*contentTypes, ",")
|
||||
}
|
||||
if *passRequestHeaders != "" {
|
||||
p.PassRequestHeaders = strings.Split(*passRequestHeaders, ",")
|
||||
}
|
||||
if *passResponseHeaders != "" {
|
||||
p.PassResponseHeaders = strings.Split(*passResponseHeaders, ",")
|
||||
} else {
|
||||
// set to a non-nil empty slice to pass no headers.
|
||||
p.PassResponseHeaders = []string{}
|
||||
}
|
||||
p.SignatureKeys = signatureKeys
|
||||
if *baseURL != "" {
|
||||
var err error
|
||||
|
|
@ -85,16 +97,32 @@ func main() {
|
|||
p.ScaleUp = *scaleUp
|
||||
p.Verbose = *verbose
|
||||
p.UserAgent = *userAgent
|
||||
p.MinimumCacheDuration = *minCacheDuration
|
||||
p.ForceCache = *forceCache
|
||||
|
||||
var ln net.Listener
|
||||
var err error
|
||||
|
||||
if path, ok := strings.CutPrefix(*addr, "unix:"); ok {
|
||||
ln, err = net.Listen("unix", path)
|
||||
} else {
|
||||
ln, err = net.Listen("tcp", *addr)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("listen failed: %v", err)
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Addr: *addr,
|
||||
Handler: p,
|
||||
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
}
|
||||
|
||||
r := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
||||
r.PathPrefix("/").Handler(p)
|
||||
fmt.Printf("imageproxy listening on %s\n", server.Addr)
|
||||
log.Fatal(http.ListenAndServe(*addr, r))
|
||||
fmt.Printf("imageproxy listening on %s\n", *addr)
|
||||
log.Fatal(server.Serve(ln))
|
||||
}
|
||||
|
||||
type signatureKeyList [][]byte
|
||||
|
|
@ -109,7 +137,7 @@ func (skl *signatureKeyList) Set(value string) error {
|
|||
if strings.HasPrefix(v, "@") {
|
||||
file := strings.TrimPrefix(v, "@")
|
||||
var err error
|
||||
key, err = ioutil.ReadFile(file)
|
||||
key, err = os.ReadFile(file)
|
||||
if err != nil {
|
||||
log.Fatalf("error reading signature file: %v", err)
|
||||
}
|
||||
|
|
@ -157,7 +185,7 @@ func parseCache(c string) (imageproxy.Cache, error) {
|
|||
|
||||
u, err := url.Parse(c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing cache flag: %v", err)
|
||||
return nil, fmt.Errorf("error parsing cache flag: %w", err)
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
|
|
|
|||
161
data.go
161
data.go
|
|
@ -4,6 +4,7 @@
|
|||
package imageproxy
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
|
@ -11,6 +12,8 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -30,6 +33,8 @@ const (
|
|||
optCropWidth = "cw"
|
||||
optCropHeight = "ch"
|
||||
optSmartCrop = "sc"
|
||||
optTrim = "trim"
|
||||
optValidUntil = "vu"
|
||||
)
|
||||
|
||||
// URLError reports a malformed URL error.
|
||||
|
|
@ -80,6 +85,12 @@ type Options struct {
|
|||
|
||||
// Automatically find good crop points based on image content.
|
||||
SmartCrop bool
|
||||
|
||||
// If true, automatically trim pixels of the same color around the edges
|
||||
Trim bool
|
||||
|
||||
// If non-zero, the URL is valid until this time.
|
||||
ValidUntil time.Time
|
||||
}
|
||||
|
||||
func (o Options) String() string {
|
||||
|
|
@ -123,7 +134,15 @@ func (o Options) String() string {
|
|||
if o.SmartCrop {
|
||||
opts = append(opts, optSmartCrop)
|
||||
}
|
||||
if o.Trim {
|
||||
opts = append(opts, optTrim)
|
||||
}
|
||||
if !o.ValidUntil.IsZero() {
|
||||
opts = append(opts, fmt.Sprintf("%s%d", optValidUntil, o.ValidUntil.Unix()))
|
||||
}
|
||||
|
||||
sort.Strings(opts)
|
||||
|
||||
return strings.Join(opts, ",")
|
||||
}
|
||||
|
||||
|
|
@ -132,21 +151,21 @@ func (o Options) String() string {
|
|||
// the presence of other fields (like Fit). A non-empty Format value is
|
||||
// assumed to involve a transformation.
|
||||
func (o Options) transform() bool {
|
||||
return o.Width != 0 || o.Height != 0 || o.Rotate != 0 || o.FlipHorizontal || o.FlipVertical || o.Quality != 0 || o.Format != "" || o.CropX != 0 || o.CropY != 0 || o.CropWidth != 0 || o.CropHeight != 0
|
||||
return o.Width != 0 || o.Height != 0 || o.Rotate != 0 || o.FlipHorizontal || o.FlipVertical || o.Quality != 0 || o.Format != "" || o.CropX != 0 || o.CropY != 0 || o.CropWidth != 0 || o.CropHeight != 0 || o.Trim
|
||||
}
|
||||
|
||||
// ParseOptions parses str as a list of comma separated transformation options.
|
||||
// The options can be specified in in order, with duplicate options overwriting
|
||||
// previous values.
|
||||
//
|
||||
// Rectangle Crop
|
||||
// # Rectangle Crop
|
||||
//
|
||||
// There are four options controlling rectangle crop:
|
||||
//
|
||||
// cx{x} - X coordinate of top left rectangle corner (default: 0)
|
||||
// cy{y} - Y coordinate of top left rectangle corner (default: 0)
|
||||
// cw{width} - rectangle width (default: image width)
|
||||
// ch{height} - rectangle height (default: image height)
|
||||
// cx{x} - X coordinate of top left rectangle corner (default: 0)
|
||||
// cy{y} - Y coordinate of top left rectangle corner (default: 0)
|
||||
// cw{width} - rectangle width (default: image width)
|
||||
// ch{height} - rectangle height (default: image height)
|
||||
//
|
||||
// For all options, integer values are interpreted as exact pixel values and
|
||||
// floats between 0 and 1 are interpreted as percentages of the original image
|
||||
|
|
@ -157,13 +176,13 @@ func (o Options) transform() bool {
|
|||
// crop width or height will be adjusted, preserving the specified cx and cy
|
||||
// values. Rectangular crop is applied before any other transformations.
|
||||
//
|
||||
// Smart Crop
|
||||
// # Smart Crop
|
||||
//
|
||||
// The "sc" option will perform a content-aware smart crop to fit the
|
||||
// requested image width and height dimensions (see Size and Cropping below).
|
||||
// The smart crop option will override any requested rectangular crop.
|
||||
//
|
||||
// Size and Cropping
|
||||
// # Size and Cropping
|
||||
//
|
||||
// The size option takes the general form "{width}x{height}", where width and
|
||||
// height are numbers. Integer values greater than 1 are interpreted as exact
|
||||
|
|
@ -192,7 +211,7 @@ func (o Options) transform() bool {
|
|||
// option with only one of either width or height does the same thing as if
|
||||
// "fit" had not been specified.
|
||||
//
|
||||
// Rotation and Flips
|
||||
// # Rotation and Flips
|
||||
//
|
||||
// The "r{degrees}" option will rotate the image the specified number of
|
||||
// degrees, counter-clockwise. Valid degrees values are 90, 180, and 270.
|
||||
|
|
@ -200,17 +219,17 @@ func (o Options) transform() bool {
|
|||
// The "fv" option will flip the image vertically. The "fh" option will flip
|
||||
// the image horizontally. Images are flipped after being rotated.
|
||||
//
|
||||
// Quality
|
||||
// # Quality
|
||||
//
|
||||
// The "q{qualityPercentage}" option can be used to specify the quality of the
|
||||
// output file (JPEG only). If not specified, the default value of "95" is used.
|
||||
//
|
||||
// Format
|
||||
// # Format
|
||||
//
|
||||
// The "jpeg", "png", and "tiff" options can be used to specify the desired
|
||||
// The "jpeg", "png", and "tiff" options can be used to specify the desired
|
||||
// image format of the proxied image.
|
||||
//
|
||||
// Signature
|
||||
// # Signature
|
||||
//
|
||||
// The "s{signature}" option specifies an optional base64 encoded HMAC used to
|
||||
// sign the remote URL in the request. The HMAC key used to verify signatures is
|
||||
|
|
@ -219,20 +238,32 @@ func (o Options) transform() bool {
|
|||
// See https://github.com/willnorris/imageproxy/blob/master/docs/url-signing.md
|
||||
// for examples of generating signatures.
|
||||
//
|
||||
// # Trim
|
||||
//
|
||||
// The "trim" option will automatically trim pixels of the same color around
|
||||
// the edges of the image. This is useful for removing borders from images
|
||||
// that have been resized or cropped. The trim option is applied before other
|
||||
// options such as cropping or resizing.
|
||||
//
|
||||
// # Valid Until
|
||||
//
|
||||
// The "vu{unixtime}" option specifies a Unix timestamp at which the request URL is no longer valid.
|
||||
// For example, "vu1800000000" would mean the URL is valid until 2027-01-15T08:00:00Z.
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// 0x0 - no resizing
|
||||
// 200x - 200 pixels wide, proportional height
|
||||
// x0.15 - 15% original height, proportional width
|
||||
// 100x150 - 100 by 150 pixels, cropping as needed
|
||||
// 100 - 100 pixels square, cropping as needed
|
||||
// 150,fit - scale to fit 150 pixels square, no cropping
|
||||
// 100,r90 - 100 pixels square, rotated 90 degrees
|
||||
// 100,fv,fh - 100 pixels square, flipped horizontal and vertical
|
||||
// 200x,q60 - 200 pixels wide, proportional height, 60% quality
|
||||
// 200x,png - 200 pixels wide, converted to PNG format
|
||||
// cw100,ch100 - crop image to 100px square, starting at (0,0)
|
||||
// cx10,cy20,cw100,ch200 - crop image starting at (10,20) is 100px wide and 200px tall
|
||||
// 0x0 - no resizing
|
||||
// 200x - 200 pixels wide, proportional height
|
||||
// x0.15 - 15% original height, proportional width
|
||||
// 100x150 - 100 by 150 pixels, cropping as needed
|
||||
// 100 - 100 pixels square, cropping as needed
|
||||
// 150,fit - scale to fit 150 pixels square, no cropping
|
||||
// 100,r90 - 100 pixels square, rotated 90 degrees
|
||||
// 100,fv,fh - 100 pixels square, flipped horizontal and vertical
|
||||
// 200x,q60 - 200 pixels wide, proportional height, 60% quality
|
||||
// 200x,png - 200 pixels wide, converted to PNG format
|
||||
// cw100,ch100 - crop image to 100px square, starting at (0,0)
|
||||
// cx10,cy20,cw100,ch200 - crop image starting at (10,20) is 100px wide and 200px tall
|
||||
func ParseOptions(str string) Options {
|
||||
var options Options
|
||||
|
||||
|
|
@ -251,6 +282,8 @@ func ParseOptions(str string) Options {
|
|||
options.Format = opt
|
||||
case opt == optSmartCrop:
|
||||
options.SmartCrop = true
|
||||
case opt == optTrim:
|
||||
options.Trim = true
|
||||
case strings.HasPrefix(opt, optRotatePrefix):
|
||||
value := strings.TrimPrefix(opt, optRotatePrefix)
|
||||
options.Rotate, _ = strconv.Atoi(value)
|
||||
|
|
@ -271,6 +304,11 @@ func ParseOptions(str string) Options {
|
|||
case strings.HasPrefix(opt, optCropHeight):
|
||||
value := strings.TrimPrefix(opt, optCropHeight)
|
||||
options.CropHeight, _ = strconv.ParseFloat(value, 64)
|
||||
case strings.HasPrefix(opt, optValidUntil):
|
||||
value := strings.TrimPrefix(opt, optValidUntil)
|
||||
if v, _ := strconv.ParseInt(value, 10, 64); v > 0 {
|
||||
options.ValidUntil = time.Unix(v, 0)
|
||||
}
|
||||
case strings.Contains(opt, optSizeDelimiter):
|
||||
size := strings.SplitN(opt, optSizeDelimiter, 2)
|
||||
if w := size[0]; w != "" {
|
||||
|
|
@ -309,22 +347,40 @@ func (r Request) String() string {
|
|||
// NewRequest parses an http.Request into an imageproxy Request. Options and
|
||||
// the remote image URL are specified in the request path, formatted as:
|
||||
// /{options}/{remote_url}. Options may be omitted, so a request path may
|
||||
// simply contain /{remote_url}. The remote URL must be an absolute "http" or
|
||||
// "https" URL, should not be URL encoded, and may contain a query string.
|
||||
// simply contain /{remote_url}.
|
||||
//
|
||||
// The remote URL may be included in plain text without any encoding,
|
||||
// percent-encoded (aka URL encoded), or base64 encoded (URL safe, no padding).
|
||||
//
|
||||
// When no encoding is used, any URL query string is treated as part of the remote URL.
|
||||
// For example, given the proxy URL of `http://localhost/x/http://example.com/?id=1`,
|
||||
// the remote URL is `http://example.com/?id=1`.
|
||||
//
|
||||
// When percent-encoding is used, the full URL must be encoded.
|
||||
// Any query string on the proxy URL is NOT included as part of the remote URL.
|
||||
// Percent-encoded URLs must be absolute URLs;
|
||||
// they cannot be relative URLs used with a default base URL.
|
||||
//
|
||||
// When base64 encoding is used, the full URL must be encoded.
|
||||
// Any query string on the proxy URL is NOT included as part of the remote URL.
|
||||
// Base64 encoded URLs may be relative URLs used with a default base URL.
|
||||
//
|
||||
// Assuming an imageproxy server running on localhost, the following are all
|
||||
// valid imageproxy requests:
|
||||
//
|
||||
// http://localhost/100x200/http://example.com/image.jpg
|
||||
// http://localhost/100x200,r90/http://example.com/image.jpg?foo=bar
|
||||
// http://localhost//http://example.com/image.jpg
|
||||
// http://localhost/http://example.com/image.jpg
|
||||
// http://localhost/100x200/http://example.com/image.jpg
|
||||
// http://localhost/100x200,r90/http://example.com/image.jpg?foo=bar
|
||||
// http://localhost//http://example.com/image.jpg
|
||||
// http://localhost/http://example.com/image.jpg
|
||||
// http://localhost/x/http%3A%2F%2Fexample.com%2Fimage.jpg
|
||||
// http://localhost/100x200/aHR0cDovL2V4YW1wbGUuY29tL2ltYWdlLmpwZw
|
||||
func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) {
|
||||
var err error
|
||||
req := &Request{Original: r}
|
||||
var enc bool // whether the remote URL was base64 or URL encoded
|
||||
|
||||
path := r.URL.EscapedPath()[1:] // strip leading slash
|
||||
req.URL, err = parseURL(path)
|
||||
req.URL, enc, err = parseURL(path, baseURL)
|
||||
if err != nil || !req.URL.IsAbs() {
|
||||
// first segment should be options
|
||||
parts := strings.SplitN(path, "/", 2)
|
||||
|
|
@ -333,7 +389,7 @@ func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) {
|
|||
}
|
||||
|
||||
var err error
|
||||
req.URL, err = parseURL(parts[1])
|
||||
req.URL, enc, err = parseURL(parts[1], baseURL)
|
||||
if err != nil {
|
||||
return nil, URLError{fmt.Sprintf("unable to parse remote URL: %v", err), r.URL}
|
||||
}
|
||||
|
|
@ -353,16 +409,47 @@ func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) {
|
|||
return nil, URLError{"remote URL must have http or https scheme", r.URL}
|
||||
}
|
||||
|
||||
// query string is always part of the remote URL
|
||||
req.URL.RawQuery = r.URL.RawQuery
|
||||
if !enc {
|
||||
// if the remote URL was not base64 or URL encoded,
|
||||
// then the query string is part of the remote URL
|
||||
req.URL.RawQuery = r.URL.RawQuery
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
var reCleanedURL = regexp.MustCompile(`^(https?):/+([^/])`)
|
||||
var reIsEncodedURL = regexp.MustCompile(`^(?i)https?%3A%2F`)
|
||||
|
||||
// parseURL parses s as a URL, handling URLs that have been munged by
|
||||
// path.Clean or a webserver that collapses multiple slashes.
|
||||
func parseURL(s string) (*url.URL, error) {
|
||||
// The returned enc bool indicates whether the remote URL was encoded.
|
||||
func parseURL(s string, baseURL *url.URL) (_ *url.URL, enc bool, _ error) {
|
||||
// Try to base64 decode the string. If it is not base64 encoded,
|
||||
// this will fail quickly on the first invalid character like ":", ".", or "/".
|
||||
// Accept the decoded string if it looks like an absolute HTTP URL,
|
||||
// or if we have a baseURL and the decoded string did not contain invalid code points.
|
||||
// This allows for values like "/path", which do successfully base64 decode,
|
||||
// but not to valid code points, to be treated as an unencoded string.
|
||||
if b, err := base64.RawURLEncoding.DecodeString(s); err == nil {
|
||||
d := string(b)
|
||||
if strings.HasPrefix(d, "http://") || strings.HasPrefix(d, "https://") {
|
||||
enc = true
|
||||
s = d
|
||||
} else if baseURL != nil && !strings.ContainsRune(d, unicode.ReplacementChar) {
|
||||
enc = true
|
||||
s = d
|
||||
}
|
||||
}
|
||||
|
||||
// If the string looks like a URL encoded absolute HTTP(S) URL, decode it.
|
||||
if reIsEncodedURL.MatchString(s) {
|
||||
if u, err := url.PathUnescape(s); err == nil {
|
||||
enc = true
|
||||
s = u
|
||||
}
|
||||
}
|
||||
|
||||
s = reCleanedURL.ReplaceAllString(s, "$1://$2")
|
||||
return url.Parse(s)
|
||||
u, err := url.Parse(s)
|
||||
return u, enc, err
|
||||
}
|
||||
|
|
|
|||
94
data_test.go
94
data_test.go
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var emptyOptions = Options{}
|
||||
|
|
@ -25,8 +26,8 @@ func TestOptions_String(t *testing.T) {
|
|||
"1x2,fh,fit,fv,q80,r90",
|
||||
},
|
||||
{
|
||||
Options{Width: 0.15, Height: 1.3, Rotate: 45, Quality: 95, Signature: "c0ffee", Format: "png"},
|
||||
"0.15x1.3,png,q95,r45,sc0ffee",
|
||||
Options{Width: 0.15, Height: 1.3, Rotate: 45, Quality: 95, Signature: "c0ffee", Format: "png", ValidUntil: time.Unix(123, 0)},
|
||||
"0.15x1.3,png,q95,r45,sc0ffee,vu123",
|
||||
},
|
||||
{
|
||||
Options{Width: 0.15, Height: 1.3, CropX: 100, CropY: 200},
|
||||
|
|
@ -86,7 +87,7 @@ func TestParseOptions(t *testing.T) {
|
|||
// flags, in different orders
|
||||
{"q70,1x2,fit,r90,fv,fh,sc0ffee,png", Options{Width: 1, Height: 2, Fit: true, Rotate: 90, FlipVertical: true, FlipHorizontal: true, Quality: 70, Signature: "c0ffee", Format: "png"}},
|
||||
{"r90,fh,sc0ffee,png,q90,1x2,fv,fit", Options{Width: 1, Height: 2, Fit: true, Rotate: 90, FlipVertical: true, FlipHorizontal: true, Quality: 90, Signature: "c0ffee", Format: "png"}},
|
||||
{"cx100,cw300,1x2,cy200,ch400,sc,scaleUp", Options{Width: 1, Height: 2, ScaleUp: true, CropX: 100, CropY: 200, CropWidth: 300, CropHeight: 400, SmartCrop: true}},
|
||||
{"cx100,cw300,1x2,cy200,ch400,sc,scaleUp,vu1234567890", Options{Width: 1, Height: 2, ScaleUp: true, CropX: 100, CropY: 200, CropWidth: 300, CropHeight: 400, SmartCrop: true, ValidUntil: time.Unix(1234567890, 0)}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -152,10 +153,68 @@ func TestNewRequest(t *testing.T) {
|
|||
"http://localhost/http:///example.com/foo",
|
||||
"http://example.com/foo", emptyOptions, false,
|
||||
},
|
||||
// base64 encoded paths
|
||||
{
|
||||
"http://localhost/aHR0cDovL2V4YW1wbGUuY29tL2Zvbw",
|
||||
"http://example.com/foo", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost//aHR0cDovL2V4YW1wbGUuY29tL2Zvbw",
|
||||
"http://example.com/foo", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/x/aHR0cDovL2V4YW1wbGUuY29tL2Zvbw",
|
||||
"http://example.com/foo", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/x/aHR0cHM6Ly9leGFtcGxlLmNvbS9mb28_YmFy",
|
||||
"https://example.com/foo?bar", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/x/aHR0cHM6Ly9leGFtcGxlLmNvbS9mb28_YmFy?baz",
|
||||
"https://example.com/foo?bar", emptyOptions, false,
|
||||
},
|
||||
{ // escaped path
|
||||
"http://localhost/http://example.com/%2C",
|
||||
"http://example.com/%2C", emptyOptions, false,
|
||||
},
|
||||
// percent encoded cases
|
||||
{
|
||||
"http://localhost/1x2/http%3A%2F%2Fexample.com%2Ffoo",
|
||||
"http://example.com/foo", Options{Width: 1, Height: 2}, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/1x2/http%3A%2F%2Fexample.com%2Fhttp%2Fstuff",
|
||||
"http://example.com/http/stuff", Options{Width: 1, Height: 2}, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/http%3A%2F%2Fexample.com%2Ffoo",
|
||||
"http://example.com/foo", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/HTTP%3a%2f%2fexample.com%2Ffoo",
|
||||
"http://example.com/foo", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/http%3A%2Fexample.com%2Ffoo",
|
||||
"http://example.com/foo", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/http%3A%2F%2F%2Fexample.com%2Ffoo",
|
||||
"http://example.com/foo", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost//http%3A%2F%2Fexample.com%2Ffoo",
|
||||
"http://example.com/foo", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/http%3A%2F%2Fexample.com%2Ffoo%3Ftest%3D1%26test%3D2",
|
||||
"http://example.com/foo?test=1&test=2", emptyOptions, false,
|
||||
},
|
||||
{
|
||||
"http://localhost/1x2/http%3A%2F%2Fexample.com%2Ffoo%3Ftest%3D1%26test%3D2",
|
||||
"http://example.com/foo?test=1&test=2", Options{Width: 1, Height: 2}, false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -186,16 +245,31 @@ func TestNewRequest(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewRequest_BaseURL(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/x/path", nil)
|
||||
base, _ := url.Parse("https://example.com/")
|
||||
|
||||
r, err := NewRequest(req, base)
|
||||
if err != nil {
|
||||
t.Errorf("NewRequest(%v, %v) returned unexpected error: %v", req, base, err)
|
||||
tests := []struct {
|
||||
path string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
path: "/x/path",
|
||||
want: "https://example.com/path#0x0",
|
||||
},
|
||||
{ // Chinese characters 已然
|
||||
path: "/x/5bey54S2",
|
||||
want: "https://example.com/%E5%B7%B2%E7%84%B6#0x0",
|
||||
},
|
||||
}
|
||||
|
||||
want := "https://example.com/path#0x0"
|
||||
if got := r.String(); got != want {
|
||||
t.Errorf("NewRequest(%v, %v) returned %q, want %q", req, base, got, want)
|
||||
for _, tt := range tests {
|
||||
req, _ := http.NewRequest("GET", tt.path, nil)
|
||||
r, err := NewRequest(req, base)
|
||||
if err != nil {
|
||||
t.Errorf("NewRequest(%v, %v) returned unexpected error: %v", req, base, err)
|
||||
}
|
||||
|
||||
if got := r.String(); got != tt.want {
|
||||
t.Errorf("NewRequest(%v, %v) returned %q, want %q", req, base, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,314 +1,360 @@
|
|||
# Changelog
|
||||
|
||||
This file contains all notable changes to
|
||||
[imageproxy](https://github.com/willnorris/imageproxy). The format is based on
|
||||
[imageproxy](https://github.com/willnorris/imageproxy). The format is based on
|
||||
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project
|
||||
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
[Unreleased]: https://github.com/willnorris/imageproxy/compare/v0.9.0...HEAD
|
||||
|
||||
## [0.10.0] (2020-04-02)
|
||||
|
||||
[0.10.0]: https://github.com/willnorris/imageproxy/compare/v0.9.0...v0.10.0
|
||||
|
||||
### Added
|
||||
- add support for multiple signature keys to support key rotation
|
||||
([ef09c1b](https://github.com/willnorris/imageproxy/commit/ef09c1b),
|
||||
[#209](https://github.com/willnorris/imageproxy/pull/209),
|
||||
[maurociancio](https://github.com/maurociancio))
|
||||
- added option to include referer header in remote requests
|
||||
([#216](https://github.com/willnorris/imageproxy/issues/216))
|
||||
- added basic support for recording prometheus metrics
|
||||
([#121](https://github.com/willnorris/imageproxy/pull/121)
|
||||
[benhaan](https://github.com/benhaan))
|
||||
|
||||
- add support for multiple signature keys to support key rotation
|
||||
([ef09c1b](https://github.com/willnorris/imageproxy/commit/ef09c1b),
|
||||
[#209](https://github.com/willnorris/imageproxy/pull/209),
|
||||
[maurociancio](https://github.com/maurociancio))
|
||||
- added option to include referer header in remote requests
|
||||
([#216](https://github.com/willnorris/imageproxy/issues/216))
|
||||
- added basic support for recording prometheus metrics
|
||||
([#121](https://github.com/willnorris/imageproxy/pull/121)
|
||||
[benhaan](https://github.com/benhaan))
|
||||
|
||||
### Fixed
|
||||
- improved content type detection for some hosts, particularly S3
|
||||
([ea95ad9](https://github.com/willnorris/imageproxy/commit/ea95ad9),
|
||||
[shahan312](https://github.com/shahan312))
|
||||
- fix signature verification for some proxied URLs
|
||||
([3589510](https://github.com/willnorris/imageproxy/commit/3589510),
|
||||
[#212](https://github.com/willnorris/imageproxy/issues/212),
|
||||
([#215](https://github.com/willnorris/imageproxy/issues/215),
|
||||
thanks to [aaronpk](https://github.com/aaronpk) for helping debug and
|
||||
[fieldistor](https://github.com/fieldistor) for the suggested fix)
|
||||
|
||||
- improved content type detection for some hosts, particularly S3
|
||||
([ea95ad9](https://github.com/willnorris/imageproxy/commit/ea95ad9),
|
||||
[shahan312](https://github.com/shahan312))
|
||||
- fix signature verification for some proxied URLs
|
||||
([3589510](https://github.com/willnorris/imageproxy/commit/3589510),
|
||||
[#212](https://github.com/willnorris/imageproxy/issues/212),
|
||||
([#215](https://github.com/willnorris/imageproxy/issues/215),
|
||||
thanks to [aaronpk](https://github.com/aaronpk) for helping debug and
|
||||
[fieldistor](https://github.com/fieldistor) for the suggested fix)
|
||||
|
||||
## [0.9.0] (2019-06-10)
|
||||
|
||||
[0.9.0]: https://github.com/willnorris/imageproxy/compare/v0.8.0...v0.9.0
|
||||
|
||||
### Added
|
||||
- allow request signatures to cover options
|
||||
([#145](https://github.com/willnorris/imageproxy/issues/145))
|
||||
- add simple imageproxy-sign tool for calculating signatures
|
||||
([e1558d5](https://github.com/willnorris/imageproxy/commit/e1558d5))
|
||||
- allow overriding the Logger used by Proxy
|
||||
([#174](https://github.com/willnorris/imageproxy/pull/174),
|
||||
[hmhealey](https://github.com/hmhealey))
|
||||
- allow using environment variables for configuration
|
||||
([50e0d11](https://github.com/willnorris/imageproxy/commit/50e0d11))
|
||||
- add support for BMP images
|
||||
([d4ba520](https://github.com/willnorris/imageproxy/commit/d4ba520))
|
||||
|
||||
- allow request signatures to cover options
|
||||
([#145](https://github.com/willnorris/imageproxy/issues/145))
|
||||
- add simple imageproxy-sign tool for calculating signatures
|
||||
([e1558d5](https://github.com/willnorris/imageproxy/commit/e1558d5))
|
||||
- allow overriding the Logger used by Proxy
|
||||
([#174](https://github.com/willnorris/imageproxy/pull/174),
|
||||
[hmhealey](https://github.com/hmhealey))
|
||||
- allow using environment variables for configuration
|
||||
([50e0d11](https://github.com/willnorris/imageproxy/commit/50e0d11))
|
||||
- add support for BMP images
|
||||
([d4ba520](https://github.com/willnorris/imageproxy/commit/d4ba520))
|
||||
|
||||
### Changed
|
||||
- improvements to docker image: run as non-privileged user, use go1.12
|
||||
compiler, and build imageproxy as a go module.
|
||||
|
||||
- options are now sorted when converting to string. This is a breaking change
|
||||
for anyone relying on the option order, and will additionally invalidate
|
||||
most cached values, since the option string is part of the cache key.
|
||||
- improvements to docker image: run as non-privileged user, use go1.12
|
||||
compiler, and build imageproxy as a go module.
|
||||
|
||||
Both the original remote image, as well as any transformations on that image
|
||||
are cached, but only the transformed images will be impacted by this change.
|
||||
This will result in imageproxy having to re-perform the transformations, but
|
||||
should not result in re-fetching the remote image, unless it has already
|
||||
otherwise expired.
|
||||
- options are now sorted when converting to string. This is a breaking change
|
||||
for anyone relying on the option order, and will additionally invalidate
|
||||
most cached values, since the option string is part of the cache key.
|
||||
|
||||
Both the original remote image, as well as any transformations on that image
|
||||
are cached, but only the transformed images will be impacted by this change.
|
||||
This will result in imageproxy having to re-perform the transformations, but
|
||||
should not result in re-fetching the remote image, unless it has already
|
||||
otherwise expired.
|
||||
|
||||
### Fixed
|
||||
- properly include Accept header on remote URL requests
|
||||
([#165](https://github.com/willnorris/imageproxy/issues/165),
|
||||
[6aca1e0](https://github.com/willnorris/imageproxy/commit/6aca1e0))
|
||||
- detect response content type if content-type header is missing
|
||||
([cf54b2c](https://github.com/willnorris/imageproxy/commit/cf54b2c))
|
||||
|
||||
- properly include Accept header on remote URL requests
|
||||
([#165](https://github.com/willnorris/imageproxy/issues/165),
|
||||
[6aca1e0](https://github.com/willnorris/imageproxy/commit/6aca1e0))
|
||||
- detect response content type if content-type header is missing
|
||||
([cf54b2c](https://github.com/willnorris/imageproxy/commit/cf54b2c))
|
||||
|
||||
### Removed
|
||||
- removed deprecated `whitelist` flag and `Proxy.Whitelist` struct field. Use
|
||||
`allowHosts` and `Proxy.AllowHosts` instead.
|
||||
|
||||
- removed deprecated `whitelist` flag and `Proxy.Whitelist` struct field. Use
|
||||
`allowHosts` and `Proxy.AllowHosts` instead.
|
||||
|
||||
## [0.8.0] (2019-03-21)
|
||||
|
||||
[0.8.0]: https://github.com/willnorris/imageproxy/compare/v0.7.0...v0.8.0
|
||||
|
||||
### Added
|
||||
- added support for restricting proxied URLs [based on Content-Type
|
||||
headers](https://github.com/willnorris/imageproxy#allowed-content-type-list)
|
||||
([#141](https://github.com/willnorris/imageproxy/pull/141),
|
||||
[ccbrown](https://github.com/ccbrown))
|
||||
- added ability to [deny requests](https://github.com/willnorris/imageproxy#allowed-and-denied-hosts-list)
|
||||
for certain remote hosts
|
||||
([#85](https://github.com/willnorris/imageproxy/pull/85),
|
||||
[geriljaSA](https://github.com/geriljaSA))
|
||||
- added `userAgent` flag to specify a custom user agent when fetching images
|
||||
([#83](https://github.com/willnorris/imageproxy/pull/83),
|
||||
[huguesalary](https://github.com/huguesalary))
|
||||
- added support for [s3 compatible](https://github.com/willnorris/imageproxy#cache)
|
||||
storage providers
|
||||
([#147](https://github.com/willnorris/imageproxy/pull/147),
|
||||
[ruledio](https://github.com/ruledio))
|
||||
- log URL when image transform fails for easier debugging
|
||||
([#149](https://github.com/willnorris/imageproxy/pull/149),
|
||||
[daohoangson](https://github.com/daohoangson))
|
||||
- added support for building imageproxy as a [go module](https://golang.org/wiki/Modules).
|
||||
A future version will remove vendored dependencies, at which point building
|
||||
as a module will be the only supported method of building imageproxy.
|
||||
|
||||
- added support for restricting proxied URLs [based on Content-Type
|
||||
headers](https://github.com/willnorris/imageproxy#allowed-content-type-list)
|
||||
([#141](https://github.com/willnorris/imageproxy/pull/141),
|
||||
[ccbrown](https://github.com/ccbrown))
|
||||
- added ability to [deny requests](https://github.com/willnorris/imageproxy#allowed-and-denied-hosts-list)
|
||||
for certain remote hosts
|
||||
([#85](https://github.com/willnorris/imageproxy/pull/85),
|
||||
[geriljaSA](https://github.com/geriljaSA))
|
||||
- added `userAgent` flag to specify a custom user agent when fetching images
|
||||
([#83](https://github.com/willnorris/imageproxy/pull/83),
|
||||
[huguesalary](https://github.com/huguesalary))
|
||||
- added support for [s3 compatible](https://github.com/willnorris/imageproxy#cache)
|
||||
storage providers
|
||||
([#147](https://github.com/willnorris/imageproxy/pull/147),
|
||||
[ruledio](https://github.com/ruledio))
|
||||
- log URL when image transform fails for easier debugging
|
||||
([#149](https://github.com/willnorris/imageproxy/pull/149),
|
||||
[daohoangson](https://github.com/daohoangson))
|
||||
- added support for building imageproxy as a [go module](https://golang.org/wiki/Modules).
|
||||
A future version will remove vendored dependencies, at which point building
|
||||
as a module will be the only supported method of building imageproxy.
|
||||
|
||||
### Changed
|
||||
- when a remote URL is denied, return a generic error message that does not specify exactly why it failed
|
||||
([7e19b5c](https://github.com/willnorris/imageproxy/commit/7e19b5c))
|
||||
|
||||
- when a remote URL is denied, return a generic error message that does not specify exactly why it failed
|
||||
([7e19b5c](https://github.com/willnorris/imageproxy/commit/7e19b5c))
|
||||
|
||||
### Deprecated
|
||||
- `whitelist` flag and `Proxy.Whitelist` struct field renamed to `allowHosts`
|
||||
and `Proxy.AllowHosts`. Old values are still supported, but will be removed
|
||||
in a future release.
|
||||
|
||||
- `whitelist` flag and `Proxy.Whitelist` struct field renamed to `allowHosts`
|
||||
and `Proxy.AllowHosts`. Old values are still supported, but will be removed
|
||||
in a future release.
|
||||
|
||||
### Fixed
|
||||
- fixed tcp_mem resource leak on 304 responses
|
||||
([#153](https://github.com/willnorris/imageproxy/pull/153),
|
||||
[Micr0mega](https://github.com/Micr0mega))
|
||||
|
||||
- fixed tcp_mem resource leak on 304 responses
|
||||
([#153](https://github.com/willnorris/imageproxy/pull/153),
|
||||
[Micr0mega](https://github.com/Micr0mega))
|
||||
|
||||
## [0.7.0] (2018-02-06)
|
||||
|
||||
[0.7.0]: https://github.com/willnorris/imageproxy/compare/v0.6.0...v0.7.0
|
||||
|
||||
### Added
|
||||
- added support for arbitrary [rectangular crops](https://godoc.org/willnorris.com/go/imageproxy#hdr-Rectangle_Crop)
|
||||
([#90](https://github.com/willnorris/imageproxy/pull/90),
|
||||
[maciejtarnowski](https://github.com/maciejtarnowski))
|
||||
- added support for tiff images
|
||||
([#109](https://github.com/willnorris/imageproxy/pull/109),
|
||||
[mikecx](https://github.com/mikecx))
|
||||
- added support for additional [caching backends](https://github.com/willnorris/imageproxy#cache):
|
||||
- Google Cloud Storage
|
||||
([#106](https://github.com/willnorris/imageproxy/pull/106),
|
||||
[diegomarangoni](https://github.com/diegomarangoni))
|
||||
- Azure
|
||||
([#79](https://github.com/willnorris/imageproxy/pull/79),
|
||||
[PaulARoy](https://github.com/PaulARoy))
|
||||
- Redis
|
||||
([#49](https://github.com/willnorris/imageproxy/issues/49)
|
||||
[dbfc693](https://github.com/willnorris/imageproxy/commit/dbfc693))
|
||||
- Tiering multiple caches by repeating the `-cache` flag
|
||||
([ec5b543](https://github.com/willnorris/imageproxy/commit/ec5b543))
|
||||
- added support for EXIF orientation tags
|
||||
([#63](https://github.com/willnorris/imageproxy/issues/63),
|
||||
[67619a6](https://github.com/willnorris/imageproxy/commit/67619a6))
|
||||
- added [smart crop feature](https://godoc.org/willnorris.com/go/imageproxy#hdr-Smart_Crop)
|
||||
([#55](https://github.com/willnorris/imageproxy/issues/55),
|
||||
[afbd254](https://github.com/willnorris/imageproxy/commit/afbd254))
|
||||
|
||||
- added support for arbitrary [rectangular crops](https://godoc.org/willnorris.com/go/imageproxy#hdr-Rectangle_Crop)
|
||||
([#90](https://github.com/willnorris/imageproxy/pull/90),
|
||||
[maciejtarnowski](https://github.com/maciejtarnowski))
|
||||
- added support for tiff images
|
||||
([#109](https://github.com/willnorris/imageproxy/pull/109),
|
||||
[mikecx](https://github.com/mikecx))
|
||||
- added support for additional [caching backends](https://github.com/willnorris/imageproxy#cache):
|
||||
- Google Cloud Storage
|
||||
([#106](https://github.com/willnorris/imageproxy/pull/106),
|
||||
[diegomarangoni](https://github.com/diegomarangoni))
|
||||
- Azure
|
||||
([#79](https://github.com/willnorris/imageproxy/pull/79),
|
||||
[PaulARoy](https://github.com/PaulARoy))
|
||||
- Redis
|
||||
([#49](https://github.com/willnorris/imageproxy/issues/49)
|
||||
[dbfc693](https://github.com/willnorris/imageproxy/commit/dbfc693))
|
||||
- Tiering multiple caches by repeating the `-cache` flag
|
||||
([ec5b543](https://github.com/willnorris/imageproxy/commit/ec5b543))
|
||||
- added support for EXIF orientation tags
|
||||
([#63](https://github.com/willnorris/imageproxy/issues/63),
|
||||
[67619a6](https://github.com/willnorris/imageproxy/commit/67619a6))
|
||||
- added [smart crop feature](https://godoc.org/willnorris.com/go/imageproxy#hdr-Smart_Crop)
|
||||
([#55](https://github.com/willnorris/imageproxy/issues/55),
|
||||
[afbd254](https://github.com/willnorris/imageproxy/commit/afbd254))
|
||||
|
||||
### Changed
|
||||
- rotate values are normalized, such that `r-90` is the same as `r270`
|
||||
([07c54b4](https://github.com/willnorris/imageproxy/commit/07c54b4))
|
||||
- now return `200 OK` response for requests to root `/`
|
||||
([5ee7e28](https://github.com/willnorris/imageproxy/commit/5ee7e28))
|
||||
- switch to using official AWS Go SDK for s3 cache storage. This is a
|
||||
breaking change for anyone using that cache implementation, since the URL
|
||||
syntax has changed. This adds support for the newer v4 auth method, as well
|
||||
as additional s3 regions.
|
||||
([0ee5167](https://github.com/willnorris/imageproxy/commit/0ee5167))
|
||||
- switched to standard go log library. Added `-verbose` flag for more logging
|
||||
in-memory cache backend supports limiting the max cache size
|
||||
([a57047f](https://github.com/willnorris/imageproxy/commit/a57047f))
|
||||
- docker image sized reduced by using scratch image and multistage build
|
||||
([#113](https://github.com/willnorris/imageproxy/pull/113),
|
||||
[matematik7](https://github.com/matematik7))
|
||||
|
||||
- rotate values are normalized, such that `r-90` is the same as `r270`
|
||||
([07c54b4](https://github.com/willnorris/imageproxy/commit/07c54b4))
|
||||
- now return `200 OK` response for requests to root `/`
|
||||
([5ee7e28](https://github.com/willnorris/imageproxy/commit/5ee7e28))
|
||||
- switch to using official AWS Go SDK for s3 cache storage. This is a
|
||||
breaking change for anyone using that cache implementation, since the URL
|
||||
syntax has changed. This adds support for the newer v4 auth method, as well
|
||||
as additional s3 regions.
|
||||
([0ee5167](https://github.com/willnorris/imageproxy/commit/0ee5167))
|
||||
- switched to standard go log library. Added `-verbose` flag for more logging
|
||||
in-memory cache backend supports limiting the max cache size
|
||||
([a57047f](https://github.com/willnorris/imageproxy/commit/a57047f))
|
||||
- docker image sized reduced by using scratch image and multistage build
|
||||
([#113](https://github.com/willnorris/imageproxy/pull/113),
|
||||
[matematik7](https://github.com/matematik7))
|
||||
|
||||
### Removed
|
||||
- removed deprecated `cacheDir` and `cacheSize` flags
|
||||
|
||||
- removed deprecated `cacheDir` and `cacheSize` flags
|
||||
|
||||
### Fixed
|
||||
- fixed interpretation of `Last-Modified` and `If-Modified-Since` headers
|
||||
([#108](https://github.com/willnorris/imageproxy/pull/108),
|
||||
[jamesreggio](https://github.com/jamesreggio))
|
||||
- preserve original URL encoding
|
||||
([#115](https://github.com/willnorris/imageproxy/issues/115))
|
||||
|
||||
- fixed interpretation of `Last-Modified` and `If-Modified-Since` headers
|
||||
([#108](https://github.com/willnorris/imageproxy/pull/108),
|
||||
[jamesreggio](https://github.com/jamesreggio))
|
||||
- preserve original URL encoding
|
||||
([#115](https://github.com/willnorris/imageproxy/issues/115))
|
||||
|
||||
## [0.6.0] (2017-08-29)
|
||||
|
||||
[0.6.0]: https://github.com/willnorris/imageproxy/compare/v0.5.1...v0.6.0
|
||||
|
||||
### Added
|
||||
- added health check endpoint
|
||||
([#54](https://github.com/willnorris/imageproxy/pull/54),
|
||||
[immunda](https://github.com/immunda))
|
||||
- preserve Link headers from remote image
|
||||
([#68](https://github.com/willnorris/imageproxy/pull/68),
|
||||
[xavren](https://github.com/xavren))
|
||||
- added support for per-request timeout
|
||||
([#75](https://github.com/willnorris/imageproxy/issues/75))
|
||||
- added support for specifying output image format
|
||||
([b9cc9df](https://github.com/willnorris/imageproxy/commit/b9cc9df))
|
||||
- added webp support (decode only)
|
||||
([3280445](https://github.com/willnorris/imageproxy/commit/3280445))
|
||||
- added CORS support
|
||||
([#96](https://github.com/willnorris/imageproxy/pull/96),
|
||||
[romdim](https://github.com/romdim))
|
||||
|
||||
- added health check endpoint
|
||||
([#54](https://github.com/willnorris/imageproxy/pull/54),
|
||||
[immunda](https://github.com/immunda))
|
||||
- preserve Link headers from remote image
|
||||
([#68](https://github.com/willnorris/imageproxy/pull/68),
|
||||
[xavren](https://github.com/xavren))
|
||||
- added support for per-request timeout
|
||||
([#75](https://github.com/willnorris/imageproxy/issues/75))
|
||||
- added support for specifying output image format
|
||||
([b9cc9df](https://github.com/willnorris/imageproxy/commit/b9cc9df))
|
||||
- added webp support (decode only)
|
||||
([3280445](https://github.com/willnorris/imageproxy/commit/3280445))
|
||||
- added CORS support
|
||||
([#96](https://github.com/willnorris/imageproxy/pull/96),
|
||||
[romdim](https://github.com/romdim))
|
||||
|
||||
### Fixed
|
||||
- improved error messages for some authorization failures
|
||||
([27d5378](https://github.com/willnorris/imageproxy/commit/27d5378))
|
||||
- skip transformation when not needed
|
||||
([#64](https://github.com/willnorris/imageproxy/issues/64))
|
||||
- properly handled "cleaned" remote URLs
|
||||
([a1af9aa](https://github.com/willnorris/imageproxy/commit/a1af9aa),
|
||||
[b61992e](https://github.com/willnorris/imageproxy/commit/b61992e))
|
||||
|
||||
- improved error messages for some authorization failures
|
||||
([27d5378](https://github.com/willnorris/imageproxy/commit/27d5378))
|
||||
- skip transformation when not needed
|
||||
([#64](https://github.com/willnorris/imageproxy/issues/64))
|
||||
- properly handled "cleaned" remote URLs
|
||||
([a1af9aa](https://github.com/willnorris/imageproxy/commit/a1af9aa),
|
||||
[b61992e](https://github.com/willnorris/imageproxy/commit/b61992e))
|
||||
|
||||
## [0.5.1] (2015-12-07)
|
||||
|
||||
[0.5.1]: https://github.com/willnorris/imageproxy/compare/v0.5.0...v0.5.1
|
||||
|
||||
### Fixed
|
||||
- fixed bug in gif resizing
|
||||
([gifresize@104a7cd](https://github.com/willnorris/gifresize/commit/104a7cd))
|
||||
|
||||
- fixed bug in gif resizing
|
||||
([gifresize@104a7cd](https://github.com/willnorris/gifresize/commit/104a7cd))
|
||||
|
||||
## [0.5.0] (2015-12-07)
|
||||
|
||||
[0.5.0]: https://github.com/willnorris/imageproxy/compare/v0.4.0...v0.5.0
|
||||
|
||||
## Added
|
||||
- added Dockerfile
|
||||
([#29](https://github.com/willnorris/imageproxy/pull/29),
|
||||
[sevki](https://github.com/sevki))
|
||||
- allow scaling image beyond its original size with `-scaleUp` flag
|
||||
([#37](https://github.com/willnorris/imageproxy/pull/37),
|
||||
[runemadsen](https://github.com/runemadsen))
|
||||
- add ability to restrict HTTP referrer
|
||||
([9213c93](https://github.com/willnorris/imageproxy/commit/9213c93),
|
||||
[connor4312](https://github.com/connor4312))
|
||||
- preserve cache-control header from remote image
|
||||
([#43](https://github.com/willnorris/imageproxy/pull/43),
|
||||
[runemadsen](https://github.com/runemadsen))
|
||||
- add support for caching images on Amazon S3
|
||||
([ec96fcb](https://github.com/willnorris/imageproxy/commit/ec96fcb)
|
||||
[victortrac](https://github.com/victortrac))
|
||||
|
||||
- added Dockerfile
|
||||
([#29](https://github.com/willnorris/imageproxy/pull/29),
|
||||
[sevki](https://github.com/sevki))
|
||||
- allow scaling image beyond its original size with `-scaleUp` flag
|
||||
([#37](https://github.com/willnorris/imageproxy/pull/37),
|
||||
[runemadsen](https://github.com/runemadsen))
|
||||
- add ability to restrict HTTP referrer
|
||||
([9213c93](https://github.com/willnorris/imageproxy/commit/9213c93),
|
||||
[connor4312](https://github.com/connor4312))
|
||||
- preserve cache-control header from remote image
|
||||
([#43](https://github.com/willnorris/imageproxy/pull/43),
|
||||
[runemadsen](https://github.com/runemadsen))
|
||||
- add support for caching images on Amazon S3
|
||||
([ec96fcb](https://github.com/willnorris/imageproxy/commit/ec96fcb)
|
||||
[victortrac](https://github.com/victortrac))
|
||||
|
||||
## Changed
|
||||
- change default cache to none, and add `-cache` flag for specifying caches.
|
||||
This deprecates the `-cacheDir` flag.
|
||||
- on-disk cache now stores files in a two-level trie. For example, for a file
|
||||
named "c0ffee", store file as "c0/ff/c0ffee".
|
||||
|
||||
- change default cache to none, and add `-cache` flag for specifying caches.
|
||||
This deprecates the `-cacheDir` flag.
|
||||
- on-disk cache now stores files in a two-level trie. For example, for a file
|
||||
named "c0ffee", store file as "c0/ff/c0ffee".
|
||||
|
||||
## Fixed
|
||||
- skip resizing if requested dimensions larger than original
|
||||
([#46](https://github.com/willnorris/imageproxy/pull/46),
|
||||
[orian](https://github.com/orian))
|
||||
|
||||
- skip resizing if requested dimensions larger than original
|
||||
([#46](https://github.com/willnorris/imageproxy/pull/46),
|
||||
[orian](https://github.com/orian))
|
||||
|
||||
## [0.4.0] (2015-05-21)
|
||||
|
||||
[0.4.0]: https://github.com/willnorris/imageproxy/compare/v0.3.0...v0.4.0
|
||||
|
||||
### Added
|
||||
- added support for animated gifs
|
||||
([#23](https://github.com/willnorris/imageproxy/issues/23))
|
||||
|
||||
- added support for animated gifs
|
||||
([#23](https://github.com/willnorris/imageproxy/issues/23))
|
||||
|
||||
### Changed
|
||||
- non-200 responses from remote servers are proxied as-is
|
||||
|
||||
- non-200 responses from remote servers are proxied as-is
|
||||
|
||||
## [0.3.0] (2015-12-07)
|
||||
|
||||
[0.3.0]: https://github.com/willnorris/imageproxy/compare/v0.2.3...v0.3.0
|
||||
|
||||
### Added
|
||||
- added support for signing requests using a sha-256 HMAC.
|
||||
([a9efefc](https://github.com/willnorris/imageproxy/commit/a9efefc))
|
||||
- more complete logging of requests and whether response is from the cache
|
||||
([#17](https://github.com/willnorris/imageproxy/issues/17))
|
||||
- added support for a base URL for remote images. This allows shorter relative
|
||||
URLs to be specified in requests.
|
||||
([#15](https://github.com/willnorris/imageproxy/issues/15))
|
||||
|
||||
- added support for signing requests using a sha-256 HMAC.
|
||||
([a9efefc](https://github.com/willnorris/imageproxy/commit/a9efefc))
|
||||
- more complete logging of requests and whether response is from the cache
|
||||
([#17](https://github.com/willnorris/imageproxy/issues/17))
|
||||
- added support for a base URL for remote images. This allows shorter relative
|
||||
URLs to be specified in requests.
|
||||
([#15](https://github.com/willnorris/imageproxy/issues/15))
|
||||
|
||||
### Fixed
|
||||
- be more precise in copying over all headers from remote image response
|
||||
([1bf0515](https://github.com/willnorris/imageproxy/commit/1bf0515))
|
||||
|
||||
- be more precise in copying over all headers from remote image response
|
||||
([1bf0515](https://github.com/willnorris/imageproxy/commit/1bf0515))
|
||||
|
||||
## [0.2.3] (2015-02-20)
|
||||
|
||||
[0.2.3]: https://github.com/willnorris/imageproxy/compare/v0.2.2...v0.2.3
|
||||
|
||||
### Added
|
||||
- added quality option
|
||||
([#13](https://github.com/willnorris/imageproxy/pull/13)
|
||||
[cubabit](https://github.com/cubabit))
|
||||
|
||||
- added quality option
|
||||
([#13](https://github.com/willnorris/imageproxy/pull/13)
|
||||
[cubabit](https://github.com/cubabit))
|
||||
|
||||
## [0.2.2] (2014-12-08)
|
||||
|
||||
[0.2.2]: https://github.com/willnorris/imageproxy/compare/v0.2.1...v0.2.2
|
||||
|
||||
### Added
|
||||
- added `cacheSize` flag to command line
|
||||
|
||||
- added `cacheSize` flag to command line
|
||||
|
||||
### Changed
|
||||
- improved documentation and error messages
|
||||
- negative width or height transformation values interpreted as 0
|
||||
|
||||
- improved documentation and error messages
|
||||
- negative width or height transformation values interpreted as 0
|
||||
|
||||
## [0.2.1] (2014-08-13)
|
||||
|
||||
[0.2.1]: https://github.com/willnorris/imageproxy/compare/v0.2.0...v0.2.1
|
||||
|
||||
### Changed
|
||||
- restructured package so that the command line tools is now installed from
|
||||
`willnorris.com/go/imageproxy/cmd/imageproxy`
|
||||
|
||||
- restructured package so that the command line tools is now installed from
|
||||
`willnorris.com/go/imageproxy/cmd/imageproxy`
|
||||
|
||||
## [0.2.0] (2014-07-02)
|
||||
|
||||
[0.2.0]: https://github.com/willnorris/imageproxy/compare/v0.1.0...v0.2.0
|
||||
|
||||
### Added
|
||||
- transformed images are cached in addition to the original image
|
||||
([#1](https://github.com/willnorris/imageproxy/issues/1))
|
||||
- support etag and last-modified headers on incoming requests
|
||||
([#3](https://github.com/willnorris/imageproxy/issues/3))
|
||||
- support wildcards in list of allowed hosts
|
||||
|
||||
- transformed images are cached in addition to the original image
|
||||
([#1](https://github.com/willnorris/imageproxy/issues/1))
|
||||
- support etag and last-modified headers on incoming requests
|
||||
([#3](https://github.com/willnorris/imageproxy/issues/3))
|
||||
- support wildcards in list of allowed hosts
|
||||
|
||||
### Changed
|
||||
- options can be specified in any order
|
||||
- images cannot be resized larger than their original dimensions
|
||||
|
||||
- options can be specified in any order
|
||||
- images cannot be resized larger than their original dimensions
|
||||
|
||||
## [0.1.0] (2013-12-26)
|
||||
|
||||
[0.1.0]: https://github.com/willnorris/imageproxy/compare/5d75e8a...v0.1.0
|
||||
|
||||
Initial release. Supported transformation options include:
|
||||
- width and height
|
||||
- different crop modes
|
||||
- rotation (in 90 degree increments)
|
||||
- flip (horizontal or vertical)
|
||||
Initial release. Supported transformation options include:
|
||||
|
||||
- width and height
|
||||
- different crop modes
|
||||
- rotation (in 90 degree increments)
|
||||
- flip (horizontal or vertical)
|
||||
|
||||
Images can be cached in-memory or on-disk.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Types of contributions
|
||||
|
||||
Simple bug fixes for existing functionality are always welcome. In many cases,
|
||||
Simple bug fixes for existing functionality are always welcome. In many cases,
|
||||
it may be helpful to include a reproducible sample case that demonstrates the
|
||||
bug being fixed.
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ the [GitHub issue tracker](https://github.com/willnorris/imageproxy/issues).
|
|||
If reporting a bug, please try and provide as much context as possible such as
|
||||
what version of imageproxy you're running, what configuration options, specific
|
||||
remote URLs that exhibit issues, and anything else that might be relevant to
|
||||
the bug. For feature requests, please explain what you're trying to do, and
|
||||
the bug. For feature requests, please explain what you're trying to do, and
|
||||
how the requested feature would help you do that.
|
||||
|
||||
Security related bugs can either be reported in the issue tracker, or if they
|
||||
|
|
@ -24,6 +24,6 @@ are more sensitive, emailed to <will@willnorris.com>.
|
|||
## Code Style and Tests
|
||||
|
||||
Go code should follow general best practices, such as using go fmt, go lint, and
|
||||
go vet (this is enforced by our continuous integration setup). Tests should
|
||||
go vet (this is enforced by our continuous integration setup). Tests should
|
||||
always be included where possible, especially for bug fixes in order to prevent
|
||||
regressions.
|
||||
|
|
|
|||
|
|
@ -4,81 +4,62 @@
|
|||
|
||||
## Objective
|
||||
|
||||
Rearchitect imageproxy to use a plugin-based system for most features like
|
||||
transformations, security, and caching. This should reduce build times and
|
||||
binary sizes in the common case, and provide a mechanism for users to easily
|
||||
add custom features that would not be added to core for various reasons.
|
||||
Re-architect imageproxy to use a plugin-based system for most features like transformations, security, and caching.
|
||||
This should reduce build times and binary sizes in the common case,
|
||||
and provide a mechanism for users to easily add custom features that would not be added to core for various reasons.
|
||||
|
||||
## Background
|
||||
|
||||
I created imageproxy to [scratch a personal itch](https://wjn.me/b/J_), I
|
||||
needed a simple way to dynamically resize images for my personal website. I
|
||||
published it as an open source projects because that's what I do, and I'm happy
|
||||
to see others finding it useful for their needs as well.
|
||||
I created imageproxy to [scratch a personal itch](https://wjn.me/b/J_), I needed a simple way to dynamically resize images for my personal website.
|
||||
I published it as an open source projects because that's what I do, and I'm happy to see others finding it useful for their needs as well.
|
||||
|
||||
But inevitably, with more users came requests for additional features because
|
||||
people have different use cases and requirements. Some of these requests were
|
||||
relatively minor, and I was happy to add them. But one of the more common
|
||||
requests was to support different caching backends. Personally, I still use the
|
||||
on-disk cache, but many people wanted to use redis or a cloud provider like
|
||||
AWS, Azure, or GCP. For a long time I was resistant to adding support for
|
||||
these, mainly out of concern for inflating build times and binary sizes. I did
|
||||
eventually relent, and
|
||||
[#49](https://github.com/willnorris/imageproxy/issues/49) tracked adding
|
||||
support for the most common backends.
|
||||
But inevitably, with more users came requests for additional features because people have different use cases and requirements.
|
||||
Some of these requests were relatively minor, and I was happy to add them.
|
||||
But one of the more common requests was to support different caching backends.
|
||||
Personally, I still use the on-disk cache, but many people wanted to use redis or a cloud provider like AWS, Azure, or GCP.
|
||||
For a long time I was resistant to adding support for these, mainly out of concern for inflating build times and binary sizes.
|
||||
I did eventually relent, and [#49] tracked adding support for the most common backends.
|
||||
|
||||
Unfortunately my concerns proved true, and build times are *significantly*
|
||||
slower (TODO: add concrete numbers) now because of all the additional cloud
|
||||
SDKs that get compiled in. I don't personally care too much about binary size,
|
||||
since I'm not running in a constrained environment, but these build times are
|
||||
really wearing on me. Additionally, there are a number of outstanding pull
|
||||
requests for relatively obscure features that I don't really want to have to
|
||||
support in the main project. And quite honestly, there are a number of obscure
|
||||
features that did get merged in over the years that I kinda wish I could rip
|
||||
back out.
|
||||
Unfortunately my concerns proved true, and build times are _significantly_ slower (TODO: add concrete numbers) now because of all the additional cloud SDKs that get compiled in.
|
||||
I don't personally care too much about binary size, since I'm not running in a constrained environment, but these build times are really wearing on me.
|
||||
Additionally, there are a number of outstanding pull requests for relatively obscure features that I don't really want to have to support in the main project.
|
||||
And quite honestly, there are a number of obscure features that did get merged in over the years that I kinda wish I could rip back out.
|
||||
|
||||
[#49]: https://github.com/willnorris/imageproxy/issues/49
|
||||
|
||||
### Plugin support in Go
|
||||
|
||||
TODO: talk about options like
|
||||
- RPC (https://github.com/hashicorp/go-plugin)
|
||||
- pkg/plugin (https://golang.org/pkg/plugin/)
|
||||
- embedded interpreter (https://github.com/robertkrimen/otto)
|
||||
- custom binaries (https://github.com/mholt/caddy,
|
||||
https://caddy.community/t/59)
|
||||
|
||||
Spoiler: I'm planning on following the Caddy approach and using custom
|
||||
binaries.
|
||||
- RPC (<https://github.com/hashicorp/go-plugin>)
|
||||
- pkg/plugin (<https://golang.org/pkg/plugin/>)
|
||||
- embedded interpreter (<https://github.com/robertkrimen/otto>)
|
||||
- custom binaries (<https://github.com/mholt/caddy>, <https://caddy.community/t/59>)
|
||||
|
||||
Spoiler: I'm planning on following the Caddy approach and using custom binaries.
|
||||
|
||||
## Design
|
||||
|
||||
I plan to model imageproxy after Caddy, moving all key functionality into
|
||||
separate plugins that register themselves with the server, and which all
|
||||
compile to a single statically-linked binary. The core project will provide a
|
||||
great number of plugins to cover all of the existing functionality. I also
|
||||
expect I'll be much more open to adding plugins for features I may not care as
|
||||
much about personally. Of course, users can also write their own plugins and
|
||||
link them in without needing to contribute them to core if they don't want to.
|
||||
I plan to model imageproxy after Caddy, moving all key functionality into separate plugins that register themselves with the server,
|
||||
and which all compile to a single statically-linked binary.
|
||||
The core project will provide a great number of plugins to cover all of the existing functionality.
|
||||
I also expect I'll be much more open to adding plugins for features I may not care as much about personally.
|
||||
Of course, users can also write their own plugins and link them in without needing to contribute them to core if they don't want to.
|
||||
|
||||
I anticipate providing two or three build configurations in core:
|
||||
- **full** - include all the plugins that are part of core (except where they
|
||||
may conflict)
|
||||
- **minimal** - some set of minimal features that only includes basic caching
|
||||
options, limited transformation options, etc
|
||||
- **my personal config** - I'll also definitely have a build that I use
|
||||
personally on my site. I may decide to just make that the "minimal" build
|
||||
and perhaps call it something different, rather than have a third
|
||||
configuration.
|
||||
|
||||
Custom configurations beyond what is provided by core can be done by creating a
|
||||
minimal main package that imports the plugins you care about and calling some
|
||||
kind of bootstrap method (similar to [what Caddy now
|
||||
does](https://caddy.community/t/59)).
|
||||
- **full** - include all the plugins that are part of core (except where they may conflict)
|
||||
- **minimal** - some set of minimal features that only includes basic caching options, limited transformation options, etc
|
||||
- **my personal config** - I'll also definitely have a build that I use personally on my site.
|
||||
I may decide to just make that the "minimal" build and perhaps call it something different, rather than have a third configuration.
|
||||
|
||||
Custom configurations beyond what is provided by core can be done by creating a minimal main package that imports the plugins you care about
|
||||
and calling some kind of bootstrap method (similar to [what Caddy now does](https://caddy.community/t/59)).
|
||||
|
||||
### Types of plugins
|
||||
|
||||
(Initially in no particular order, just capturing thoughts. Lots to do here in
|
||||
thinking through the use cases and what kind of plugin API we really need to
|
||||
provide.)
|
||||
(Initially in no particular order, just capturing thoughts.
|
||||
Lots to do here in thinking through the use cases and what kind of plugin API we really need to provide.)
|
||||
|
||||
See also issues and PRs with [label:plugins][].
|
||||
|
||||
|
|
@ -86,85 +67,75 @@ See also issues and PRs with [label:plugins][].
|
|||
|
||||
#### Caching backend
|
||||
|
||||
This is one of the most common feature requests, and is also one of the worst
|
||||
offender for inflating build times and binary sizes because of the size of the
|
||||
dependencies that are typically required. The minimal imageproxy build would
|
||||
probably only include the in-memory and on-disk caches. Anything that talked to
|
||||
an external store (redis, cloud providers, etc) would be pulled out.
|
||||
This is one of the most common feature requests, and is also one of the worst offender for inflating build times
|
||||
and binary sizes because of the size of the dependencies that are typically required.
|
||||
The minimal imageproxy build would probably only include the in-memory and on-disk caches.
|
||||
Anything that talked to an external store (redis, cloud providers, etc) would be pulled out.
|
||||
|
||||
#### Transformation engine
|
||||
|
||||
Today, imageproxy only performs transformations which can be done with pure Go
|
||||
libraries. There have been a number of requests (or at least questions) to use
|
||||
something like [vips](https://github.com/DAddYE/vips) or
|
||||
[imagemagick](https://github.com/gographics/imagick), which are both C
|
||||
libraries. They provide more options, and (likely) better performance, at the
|
||||
cost of complexity and loss of portability in using cgo. These would likely
|
||||
replace the entire transformation engine in imageproxy, so I don't know how
|
||||
they would interact with other plugins that merely extend the main engine (they
|
||||
probably wouldn't be able to interact at all).
|
||||
Today, imageproxy only performs transformations which can be done with pure Go libraries.
|
||||
There have been a number of requests (or at least questions) to use something like [vips] or [imagemagick], which are both C libraries.
|
||||
They provide more options, and (likely) better performance, at the cost of complexity and loss of portability in using cgo.
|
||||
These would likely replace the entire transformation engine in imageproxy,
|
||||
so I don't know how they would interact with other plugins that merely extend the main engine (they probably wouldn't be able to interact at all).
|
||||
|
||||
[vips]: https://github.com/DAddYE/vips
|
||||
[imagemagick]: https://github.com/gographics/imagick
|
||||
|
||||
#### Transformation options
|
||||
|
||||
Today, imageproxy performs minimal transformations, mostly around resizing,
|
||||
cropping, and rotation. It doesn't support any kind of filters, brightness or
|
||||
contrast adjustment, etc. There are go libraries for them, they're just outside
|
||||
the scope of what I originally intended imageproxy for. But I'd be happy to
|
||||
have plugins that do that kind of thing. These plugins would need to be able to
|
||||
hook into the option parsing engine so that they could register their URL
|
||||
options.
|
||||
Today, imageproxy performs minimal transformations, mostly around resizing, cropping, and rotation.
|
||||
It doesn't support any kind of filters, brightness or contrast adjustment, etc.
|
||||
There are go libraries for them, they're just outside the scope of what I originally intended imageproxy for.
|
||||
But I'd be happy to have plugins that do that kind of thing.
|
||||
These plugins would need to be able to hook into the option parsing engine so that they could register their URL options.
|
||||
|
||||
#### Image format support
|
||||
|
||||
There have been a number of requests for imge format support that require cgo
|
||||
libraries:
|
||||
There have been a number of requests for image format support that require cgo libraries:
|
||||
|
||||
- **webp encoding** - needs cgo
|
||||
[#114](https://github.com/willnorris/imageproxy/issues/114)
|
||||
- **progressive jpegs** - probably needs cgo?
|
||||
[#77](https://github.com/willnorris/imageproxy/issues/77)
|
||||
- **gif to mp4** - maybe doable in pure go, but probably belongs in a plugin
|
||||
[#136](https://github.com/willnorris/imageproxy/issues/136)
|
||||
- **HEIF** - formate used by newer iPhones
|
||||
([HEIF](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format))
|
||||
- **webp encoding** - needs cgo [#114](https://github.com/willnorris/imageproxy/issues/114)
|
||||
- **progressive jpegs** - probably needs cgo? [#77](https://github.com/willnorris/imageproxy/issues/77)
|
||||
- **gif to mp4** - maybe doable in pure go, but probably belongs in a plugin [#136](https://github.com/willnorris/imageproxy/issues/136)
|
||||
- **HEIF** - formate used by newer iPhones ([HEIF](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format))
|
||||
|
||||
#### Option parsing
|
||||
|
||||
Today, options are specified as the first component in the URL path, but
|
||||
[#66](https://github.com/willnorris/imageproxy/pull/66) proposes optionally
|
||||
moving that to a query parameter (for a good reason, actually). Maybe putting
|
||||
that in core is okay? Maybe it belongs in a plugin, in which case we'd need to
|
||||
expose an API for replacing the option parsing code entirely.
|
||||
Today, options are specified as the first component in the URL path, but [#66] proposes optionally moving that to a query parameter (for a good reason, actually).
|
||||
Maybe putting that in core is okay?
|
||||
Maybe it belongs in a plugin, in which case we'd need to expose an API for replacing the option parsing code entirely.
|
||||
|
||||
[#66]: https://github.com/willnorris/imageproxy/pull/66
|
||||
|
||||
#### Security options
|
||||
|
||||
Some people want to add a host blacklist
|
||||
[#85](https://github.com/willnorris/imageproxy/pull/85), refusal to process
|
||||
non-image files [#53](https://github.com/willnorris/imageproxy/issues/53)
|
||||
[#119](https://github.com/willnorris/imageproxy/pull/119). I don't think there
|
||||
is an issue for it, but an early fork of the project added request signing that
|
||||
was compatible with nginx's [secure link
|
||||
module](https://nginx.org/en/docs/http/ngx_http_secure_link_module.html).
|
||||
Some people want to add a host blacklist [#85], refusal to process non-image files [#53] [#119].
|
||||
I don't think there is an issue for it,
|
||||
but an early fork of the project added request signing that was compatible with nginx's [secure link module](https://nginx.org/en/docs/http/ngx_http_secure_link_module.html).
|
||||
|
||||
[#85]: https://github.com/willnorris/imageproxy/pull/85
|
||||
[#53]: https://github.com/willnorris/imageproxy/issues/53
|
||||
[#119]: https://github.com/willnorris/imageproxy/pull/119
|
||||
|
||||
### Registering Plugins
|
||||
|
||||
Plugins are loaded simply by importing their package. They should have an
|
||||
`init` func that calls `imageproxy.RegisterPlugin`:
|
||||
Plugins are loaded simply by importing their package.
|
||||
They should have an `init` func that calls `imageproxy.RegisterPlugin`:
|
||||
|
||||
``` go
|
||||
```go
|
||||
type Plugin struct {
|
||||
}
|
||||
|
||||
func RegisterPlugin(name string, plugin Plugin)
|
||||
```
|
||||
|
||||
Plugins hook into various extension points of imageproxy by implementing
|
||||
appropriate interfaces. A single plugin can hook into multiple parts of
|
||||
imageproxy by implementing multiple interfaces.
|
||||
Plugins hook into various extension points of imageproxy by implementing appropriate interfaces.
|
||||
A single plugin can hook into multiple parts of imageproxy by implementing multiple interfaces.
|
||||
|
||||
For example, two possible interfaces for security related plugins:
|
||||
|
||||
``` go
|
||||
```go
|
||||
// A RequestAuthorizer determines if a request is authorized to be processed.
|
||||
// Requests are processed before the remote resource is retrieved.
|
||||
type RequestAuthorizer interface {
|
||||
|
|
@ -186,7 +157,7 @@ type ResponseAuthorizer interface {
|
|||
|
||||
A hypothetical interface for plugins that transform images:
|
||||
|
||||
``` go
|
||||
```go
|
||||
// An ImageTransformer transforms an image.
|
||||
type ImageTransformer interface {
|
||||
// TransformImage based on the provided options and return the result.
|
||||
|
|
@ -194,6 +165,5 @@ type ImageTransformer interface {
|
|||
}
|
||||
```
|
||||
|
||||
Plugins are additionally responsible for registering any additional command
|
||||
line flags they wish to expose to the user, as well as storing any global state
|
||||
that would previously have been stored on the Proxy struct.
|
||||
Plugins are additionally responsible for registering any additional command line flags they wish to expose to the user,
|
||||
as well as storing any global state that would previously have been stored on the Proxy struct.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# How to generate signed requests
|
||||
|
||||
Signing requests allows an imageproxy instance to proxy images from arbitrary
|
||||
remote hosts, but without opening the service up for potential abuse. When
|
||||
remote hosts, but without opening the service up for potential abuse. When
|
||||
appropriately configured, the imageproxy instance will only serve requests that
|
||||
are for allowed hosts, or which have a valid signature.
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ Signatures can be calculated in two ways:
|
|||
|
||||
1. they can be calculated solely on the remote image URL, in which case any
|
||||
transformations of the image can be requested without changes to the
|
||||
signature value. This used to be the only way to sign requests, but is no
|
||||
signature value. This used to be the only way to sign requests, but is no
|
||||
longer recommended since it still leaves the imageproxy instance open to
|
||||
potential abuse.
|
||||
|
||||
|
|
@ -17,30 +17,30 @@ Signatures can be calculated in two ways:
|
|||
the requested transformation options.
|
||||
|
||||
In both cases, the signature is calculated using HMAC-SHA256 and a secret key
|
||||
which is provided to imageproxy on startup. The message to be signed is the
|
||||
which is provided to imageproxy on startup. The message to be signed is the
|
||||
remote URL, with the transformation options optionally set as the URL fragment,
|
||||
[as documented below](#Signing-options). The signature is url-safe base64
|
||||
[as documented below](#signing-options). The signature is url-safe base64
|
||||
encoded, and [provided as an option][s-option] in the imageproxy request.
|
||||
|
||||
imageproxy will accept signatures for URLs with or without options
|
||||
transparently. It's up to the publisher of the signed URLs to decide which
|
||||
transparently. It's up to the publisher of the signed URLs to decide which
|
||||
method they use to generate the URL.
|
||||
|
||||
[s-option]: https://godoc.org/willnorris.com/go/imageproxy#hdr-Signature
|
||||
[s-option]: https://pkg.go.dev/willnorris.com/go/imageproxy#hdr-Signature-ParseOptions
|
||||
|
||||
## Signing options
|
||||
|
||||
Transformation options for a proxied URL are [specified as a comma separated
|
||||
string][ParseOptions] of individual options, which can be supplied in any
|
||||
order. When calculating a signature, options should be put in their canonical
|
||||
order. When calculating a signature, options should be put in their canonical
|
||||
form, sorted in lexigraphical order (omitting the signature option itself), and
|
||||
appended to the remote URL as the URL fragment.
|
||||
|
||||
Currently, only [size option][] has a canonical form, which is
|
||||
`{width}x{height}` with the number `0` used when no value is specified. For
|
||||
`{width}x{height}` with the number `0` used when no value is specified. For
|
||||
example, a request that does not request any size option would still have a
|
||||
canonical size value of `0x0`, indicating that no size transformation is being
|
||||
performed. If only a height of 500px is requested, the canonical form would be
|
||||
performed. If only a height of 500px is requested, the canonical form would be
|
||||
`0x500`.
|
||||
|
||||
For example, requesting the remote URL of `http://example.com/image.jpg`,
|
||||
|
|
@ -57,9 +57,8 @@ the signed value would be:
|
|||
The `100` size option was put in its canonical form of `100x100`, and the
|
||||
options are sorted, moving `q75` before `r90`.
|
||||
|
||||
[ParseOptions]: https://godoc.org/willnorris.com/go/imageproxy#ParseOptions
|
||||
[size option]: https://godoc.org/willnorris.com/go/imageproxy#hdr-Size_and_Cropping
|
||||
|
||||
[ParseOptions]: https://pkg.go.dev/willnorris.com/go/imageproxy#ParseOptions
|
||||
[size option]: https://pkg.go.dev/willnorris.com/go/imageproxy#hdr-Size_and_Cropping-ParseOptions
|
||||
|
||||
## Signed options example
|
||||
|
||||
|
|
@ -76,12 +75,10 @@ and our resulting signed key is `0sR2kjyfiF1RQRj4Jm2fFa3_6SDFqdAaDEmy1oD2U-4=`
|
|||
The final url would be
|
||||
`http://localhost:8080/400x400,q40,s0sR2kjyfiF1RQRj4Jm2fFa3_6SDFqdAaDEmy1oD2U-4=/https://octodex.github.com/images/codercat.jpg`
|
||||
|
||||
|
||||
|
||||
## Language Examples
|
||||
|
||||
Here are examples of calculating signatures in a variety of languages. These
|
||||
demonstrate the HMAC-SHA256 bits, but not the option canonicalization. In each
|
||||
Here are examples of calculating signatures in a variety of languages. These
|
||||
demonstrate the HMAC-SHA256 bits, but not the option canonicalization. In each
|
||||
example, the remote URL `https://octodex.github.com/images/codercat.jpg` is
|
||||
signed using a signature key of `secretkey`.
|
||||
|
||||
|
|
@ -90,6 +87,7 @@ See also the [imageproxy-sign tool](/cmd/imageproxy-sign).
|
|||
### Go
|
||||
|
||||
main.go:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
|
|
@ -180,38 +178,40 @@ url = sys.argv[2]
|
|||
print base64.urlsafe_b64encode(hmac.new(key, msg=url, digestmod=hashlib.sha256).digest())
|
||||
```
|
||||
|
||||
````shell
|
||||
```shell
|
||||
$ python sign.py "secretkey" "https://octodex.github.com/images/codercat.jpg"
|
||||
cw34eyalj8YvpLpETxSIxv2k8QkLel2UAR5Cku2FzGM=
|
||||
````
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
||||
```javascript
|
||||
const crypto = require('crypto');
|
||||
const URLSafeBase64 = require('urlsafe-base64');
|
||||
const crypto = require("crypto");
|
||||
const URLSafeBase64 = require("urlsafe-base64");
|
||||
|
||||
let key = process.argv[2];
|
||||
let url = process.argv[3];
|
||||
console.log(URLSafeBase64.encode(crypto.createHmac('sha256', key).update(url).digest()));
|
||||
console.log(
|
||||
URLSafeBase64.encode(crypto.createHmac("sha256", key).update(url).digest()),
|
||||
);
|
||||
```
|
||||
|
||||
````shell
|
||||
```shell
|
||||
$ node sign.js "secretkey" "https://octodex.github.com/images/codercat.jpg"
|
||||
cw34eyalj8YvpLpETxSIxv2k8QkLel2UAR5Cku2FzGM=
|
||||
````
|
||||
```
|
||||
|
||||
### PHP
|
||||
|
||||
````php
|
||||
```php
|
||||
<?php
|
||||
|
||||
$key = $argv[1];
|
||||
$url = $argv[2];
|
||||
echo strtr(base64_encode(hash_hmac('sha256', $url, $key, 1)), '/+' , '_-');
|
||||
````
|
||||
```
|
||||
|
||||
````shell
|
||||
```shell
|
||||
$ php sign.php "secretkey" "https://octodex.github.com/images/codercat.jpg"
|
||||
cw34eyalj8YvpLpETxSIxv2k8QkLel2UAR5Cku2FzGM=
|
||||
````
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Description=Image Proxy
|
|||
User=www-data
|
||||
ExecStart=/usr/local/bin/imageproxy \
|
||||
-addr localhost:4593 \
|
||||
-cache memory -cache /var/cache/imageproxy \
|
||||
-cache memory -cache /var/cache/imageproxy
|
||||
Restart=on-abort
|
||||
|
||||
[Install]
|
||||
|
|
|
|||
89
go.mod
89
go.mod
|
|
@ -1,27 +1,88 @@
|
|||
module willnorris.com/go/imageproxy
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.16.1
|
||||
github.com/Azure/azure-sdk-for-go v55.3.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.19 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
cloud.google.com/go/storage v1.52.0
|
||||
github.com/PaulARoy/azurestoragecache v0.0.0-20170906084534-3c249a3ba788
|
||||
github.com/aws/aws-sdk-go v1.40.42
|
||||
github.com/die-net/lrucache v0.0.0-20190707192454-883874fe3947
|
||||
github.com/aws/aws-sdk-go v1.55.7
|
||||
github.com/die-net/lrucache v0.0.0-20220628165024-20a71bc65bf1
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dnaeon/go-vcr v1.2.0 // indirect
|
||||
github.com/fcjr/aia-transport-go v1.2.2
|
||||
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gomodule/redigo v1.9.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
|
||||
github.com/muesli/smartcrop v0.3.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/peterbourgon/diskv v0.0.0-20171120014656-2973218375c3
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6
|
||||
golang.org/x/image v0.39.0
|
||||
willnorris.com/go/gifresize v1.0.0
|
||||
)
|
||||
|
||||
go 1.13
|
||||
require (
|
||||
cel.dev/expr v0.25.1 // indirect
|
||||
cloud.google.com/go v0.120.0 // indirect
|
||||
cloud.google.com/go/auth v0.16.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
cloud.google.com/go/iam v1.5.0 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
|
||||
github.com/dnaeon/go-vcr v1.2.0 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/api v0.229.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
)
|
||||
|
|
|
|||
836
go.sum
836
go.sum
|
|
@ -1,694 +1,262 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3 h1:wPBktZFzYBcCZVARvwVKqH1uEj+aLXofJEtrb4oOsio=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.16.1 h1:sMEIc4wxvoY3NXG7Rn9iP7jb/2buJgWR1vNXCR/UPfs=
|
||||
cloud.google.com/go/storage v1.16.1/go.mod h1:LaNorbty3ehnU3rEjXSNV/NRgQA0O8Y+uh6bPe5UOk4=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v55.3.0+incompatible h1:rLKCdFMMCAXt/QZ96skZJUArYS3UDo9Qm1ZWzoDtC9E=
|
||||
github.com/Azure/azure-sdk-for-go v55.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
|
||||
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
|
||||
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
|
||||
cloud.google.com/go/auth v0.16.0 h1:Pd8P1s9WkcrBE2n/PhAwKsdrR35V3Sg2II9B+ndM3CU=
|
||||
cloud.google.com/go/auth v0.16.0/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
cloud.google.com/go/iam v1.5.0 h1:QlLcVMhbLGOjRcGe6VTGGTyQib8dRLK2B/kYNV0+2xs=
|
||||
cloud.google.com/go/iam v1.5.0/go.mod h1:U+DOtKQltF/LxPEtcDLoobcsZMilSRwR7mgNL7knOpo=
|
||||
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
|
||||
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
|
||||
cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw=
|
||||
cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw=
|
||||
cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM=
|
||||
cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc=
|
||||
cloud.google.com/go/storage v1.52.0 h1:ROpzMW/IwipKtatA69ikxibdzQSiXJrY9f6IgBa9AlA=
|
||||
cloud.google.com/go/storage v1.52.0/go.mod h1:4wrBAbAYUvYkbrf19ahGm4I5kDQhESSqN3CGEkMGvOY=
|
||||
cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE=
|
||||
cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.19 h1:7/IqD2fEYVha1EPeaiytVKhzmPV223pfkRIQUGOK2IE=
|
||||
github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||
github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
|
||||
github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
||||
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
|
||||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
|
||||
github.com/PaulARoy/azurestoragecache v0.0.0-20170906084534-3c249a3ba788 h1:OxWBmk9BZqWOHVs+hrElt/BiexDGcStcsADt0f4cUx8=
|
||||
github.com/PaulARoy/azurestoragecache v0.0.0-20170906084534-3c249a3ba788/go.mod h1:lY1dZd8HBzJ10eqKERHn3CU59tfhzcAVb2c0ZhIWSOk=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/aws/aws-sdk-go v1.40.42 h1:LbnRM4D9fAmNZxMAZx21Efpp5MKHx2BAETS+pn90lxI=
|
||||
github.com/aws/aws-sdk-go v1.40.42/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
|
||||
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/die-net/lrucache v0.0.0-20190707192454-883874fe3947 h1:U/5Sq2nJQ0XDyks+8ATghtHSuquIGq7JYrqSrvtR2dg=
|
||||
github.com/die-net/lrucache v0.0.0-20190707192454-883874fe3947/go.mod h1:KsMcjmY1UCGl7ozPbdVPDOvLaFeXnptSvtNRczhxNto=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/die-net/lrucache v0.0.0-20220628165024-20a71bc65bf1 h1:1nCGINecpltGpOWruhy+Ac2/FRy+p1igMylF+MsijpI=
|
||||
github.com/die-net/lrucache v0.0.0-20220628165024-20a71bc65bf1/go.mod h1:NQKJ1XiOlLRLoAeq/5LE3GBlSukAK3zDUUlrvc2rfCQ=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||
github.com/fcjr/aia-transport-go v1.2.2 h1:sIZqXcM+YhTd2BDtkV2OJaqbcIVcPv1oKru3VJPIPc8=
|
||||
github.com/fcjr/aia-transport-go v1.2.2/go.mod h1:onSqSq3tGkM14WusDx7q9FTheS9R1KBtD+QBWI6zG/w=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
|
||||
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
|
||||
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc=
|
||||
github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78RwcQLUkocpI=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/peterbourgon/diskv v0.0.0-20171120014656-2973218375c3 h1:ZKRE3mqKoxObHs5oWjLnA1WxXhmlDDAVuE0VsuLIoNk=
|
||||
github.com/peterbourgon/diskv v0.0.0-20171120014656-2973218375c3/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 h1:PB3Zrjs1sG1GBX51SXyTSoOTqcDglmsk7nT6tkKPb/k=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0/go.mod h1:U2R3XyVPzn0WX7wOIypPuptulsMcPDPs/oiSVOMVnHY=
|
||||
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
|
||||
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI=
|
||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=
|
||||
golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
|
||||
golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
|
||||
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a h1:4Kd8OPUx1xgUwrHDaviWZO8MsgoZTZYC3g+8m16RBww=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0 h1:ECJUVngj71QI6XEm7b1sAf8BljU5inEhMbKPR8Lxhhk=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210825212027-de86158e7fda h1:iT5uhT54PtbqUsWddv/nnEWdE5e/MTr+Nv3vjxlBP1A=
|
||||
google.golang.org/genproto v0.0.0-20210825212027-de86158e7fda/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.229.0 h1:p98ymMtqeJ5i3lIBMj5MpR9kzIIgzpHHh8vQ+vgAzx8=
|
||||
google.golang.org/api v0.229.0/go.mod h1:wyDfmq5g1wYJWn29O22FDWN48P7Xcz0xz+LBpptYvB0=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
willnorris.com/go/gifresize v1.0.0 h1:GKS68zjNhHMqkgNTv4iFAO/j/sNcVSOHQ7SqmDAIAmM=
|
||||
willnorris.com/go/gifresize v1.0.0/go.mod h1:eBM8gogBGCcaH603vxSpnfjwXIpq6nmnj/jauBDKtAk=
|
||||
|
|
|
|||
200
imageproxy.go
200
imageproxy.go
|
|
@ -14,13 +14,13 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -29,8 +29,12 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
tphttp "willnorris.com/go/imageproxy/third_party/http"
|
||||
tphc "willnorris.com/go/imageproxy/third_party/httpcache"
|
||||
)
|
||||
|
||||
// Maximum number of redirection-followings allowed.
|
||||
const maxRedirects = 10
|
||||
|
||||
// Proxy serves image requests.
|
||||
type Proxy struct {
|
||||
Client *http.Client // client used to fetch remote URLs
|
||||
|
|
@ -85,6 +89,25 @@ type Proxy struct {
|
|||
|
||||
// The User-Agent used by imageproxy when requesting origin image
|
||||
UserAgent string
|
||||
|
||||
// PassRequestHeaders identifies HTTP headers to pass from inbound
|
||||
// requests to the proxied server.
|
||||
PassRequestHeaders []string
|
||||
|
||||
// PassResponseHeaders identifies HTTP headers to pass from server responses to the proxy client.
|
||||
// If nil, a default set of headers is passed: Cache-Control, Last-Modified, Expires, Etag, Link.
|
||||
PassResponseHeaders []string
|
||||
|
||||
// MinimumCacheDuration is the minimum duration to cache remote images.
|
||||
// This will override cache duration from the remote server.
|
||||
MinimumCacheDuration time.Duration
|
||||
|
||||
// ForceCache, when true, forces caching of all images, even if the
|
||||
// remote server specifies 'private' or 'no-store' in the cache-control
|
||||
// header.
|
||||
ForceCache bool
|
||||
|
||||
timeNow time.Time // current time, used for testing
|
||||
}
|
||||
|
||||
// NewProxy constructs a new proxy. The provided http RoundTripper will be
|
||||
|
|
@ -107,11 +130,13 @@ func NewProxy(transport http.RoundTripper, cache Cache) *Proxy {
|
|||
Transport: &TransformingTransport{
|
||||
Transport: transport,
|
||||
CachingClient: client,
|
||||
log: func(format string, v ...interface{}) {
|
||||
limiter: make(chan struct{}, runtime.NumCPU()),
|
||||
log: func(format string, v ...any) {
|
||||
if proxy.Verbose {
|
||||
proxy.logf(format, v...)
|
||||
}
|
||||
},
|
||||
updateCacheHeaders: proxy.updateCacheHeaders,
|
||||
},
|
||||
Cache: cache,
|
||||
MarkCachedResponses: true,
|
||||
|
|
@ -122,6 +147,63 @@ func NewProxy(transport http.RoundTripper, cache Cache) *Proxy {
|
|||
return proxy
|
||||
}
|
||||
|
||||
// updateCacheHeaders updates the cache-control headers in the provided headers.
|
||||
//
|
||||
// If the cache-control header includes the 'private' directive,
|
||||
// then 'no-store' is added to the header to prevent caching.
|
||||
// If p.ForceCache is set, then 'private' and 'no-store' are both ignored and removed.
|
||||
//
|
||||
// This method also sets the cache-control max-age value to the maximum of the minimum cache
|
||||
// duration, the expires header, and the max-age header. It also removes the
|
||||
// expires header.
|
||||
func (p *Proxy) updateCacheHeaders(hdr http.Header) {
|
||||
cc := tphc.ParseCacheControl(hdr)
|
||||
|
||||
// respect 'private' and 'no-store' directives unless ForceCache is set.
|
||||
// The httpcache package ignores the 'private' directive,
|
||||
// since it's not intended to be used as a shared cache.
|
||||
// imageproxy IS a shared cache, so we enforce the 'private' directive ourself
|
||||
// by setting 'no-store', which httpcache does respect.
|
||||
if p.ForceCache {
|
||||
delete(cc, "private")
|
||||
delete(cc, "no-store")
|
||||
hdr.Set("Cache-Control", cc.String())
|
||||
} else {
|
||||
if _, ok := cc["private"]; ok {
|
||||
cc["no-store"] = ""
|
||||
hdr.Set("Cache-Control", cc.String())
|
||||
return
|
||||
}
|
||||
if _, ok := cc["no-store"]; ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if p.MinimumCacheDuration == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var expiresDuration time.Duration
|
||||
var maxAgeDuration time.Duration
|
||||
|
||||
if maxAge, ok := cc["max-age"]; ok {
|
||||
maxAgeDuration, _ = time.ParseDuration(maxAge + "s")
|
||||
}
|
||||
if date, err := httpcache.Date(hdr); err == nil {
|
||||
if expiresHeader := hdr.Get("Expires"); expiresHeader != "" {
|
||||
if expires, err := time.Parse(time.RFC1123, expiresHeader); err == nil {
|
||||
expiresDuration = expires.Sub(date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maxAge := max(p.MinimumCacheDuration, expiresDuration, maxAgeDuration)
|
||||
cc["max-age"] = fmt.Sprintf("%d", int(maxAge.Seconds()))
|
||||
|
||||
hdr.Set("Cache-Control", cc.String())
|
||||
hdr.Del("Expires")
|
||||
}
|
||||
|
||||
// ServeHTTP handles incoming requests.
|
||||
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/favicon.ico" {
|
||||
|
|
@ -134,7 +216,7 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if r.URL.Path == "/metrics" {
|
||||
var h http.Handler = promhttp.Handler()
|
||||
var h = promhttp.Handler()
|
||||
h.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
|
@ -145,7 +227,12 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
timer := prometheus.NewTimer(metricRequestDuration)
|
||||
defer timer.ObserveDuration()
|
||||
metricRequestsInFlight.Inc()
|
||||
defer func() {
|
||||
timer.ObserveDuration()
|
||||
metricRequestsInFlight.Dec()
|
||||
}()
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
|
|
@ -179,10 +266,19 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
// pass along the referer header from the original request
|
||||
copyHeader(actualReq.Header, r.Header, "referer")
|
||||
}
|
||||
if len(p.PassRequestHeaders) != 0 {
|
||||
copyHeader(actualReq.Header, r.Header, p.PassRequestHeaders...)
|
||||
}
|
||||
if p.FollowRedirects {
|
||||
// FollowRedirects is true (default), ensure that the redirected host is allowed
|
||||
p.Client.CheckRedirect = func(newreq *http.Request, via []*http.Request) error {
|
||||
if hostMatches(p.DenyHosts, newreq.URL) || (len(p.AllowHosts) > 0 && !hostMatches(p.AllowHosts, newreq.URL)) {
|
||||
if len(via) > maxRedirects {
|
||||
if p.Verbose {
|
||||
p.logf("followed too many redirects (%d).", len(via))
|
||||
}
|
||||
return errTooManyRedirects
|
||||
}
|
||||
if hostMatches(p.DenyHosts, newreq.URL) {
|
||||
http.Error(w, msgNotAllowedInRedirect, http.StatusForbidden)
|
||||
return errNotAllowed
|
||||
}
|
||||
|
|
@ -195,7 +291,6 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
resp, err := p.Client.Do(actualReq)
|
||||
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("error fetching remote image: %v", err)
|
||||
p.log(msg)
|
||||
|
|
@ -206,6 +301,12 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
// close the original resp.Body, even if we wrap it in a NopCloser below
|
||||
defer resp.Body.Close()
|
||||
|
||||
// return early on 404s. Perhaps handle additional status codes here?
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
cached := resp.Header.Get(httpcache.XFromCache) == "1"
|
||||
if p.Verbose {
|
||||
p.logf("request: %+v (served from cache: %t)", *actualReq, cached)
|
||||
|
|
@ -215,7 +316,12 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
metricServedFromCache.Inc()
|
||||
}
|
||||
|
||||
copyHeader(w.Header(), resp.Header, "Cache-Control", "Last-Modified", "Expires", "Etag", "Link")
|
||||
if p.PassResponseHeaders == nil {
|
||||
// pass default set of response headers
|
||||
copyHeader(w.Header(), resp.Header, "Cache-Control", "Last-Modified", "Expires", "Etag", "Link")
|
||||
} else {
|
||||
copyHeader(w.Header(), resp.Header, p.PassResponseHeaders...)
|
||||
}
|
||||
|
||||
if should304(r, resp) {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
|
|
@ -226,7 +332,7 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
if contentType == "" || contentType == "application/octet-stream" || contentType == "binary/octet-stream" {
|
||||
// try to detect content type
|
||||
b := bufio.NewReader(resp.Body)
|
||||
resp.Body = ioutil.NopCloser(b)
|
||||
resp.Body = io.NopCloser(b)
|
||||
contentType = peekContentType(b)
|
||||
}
|
||||
if resp.ContentLength != 0 && !contentTypeMatches(p.ContentTypes, contentType) {
|
||||
|
|
@ -260,23 +366,17 @@ func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
|
|||
// the content type. Returns empty string if error occurs.
|
||||
func peekContentType(p *bufio.Reader) string {
|
||||
byt, err := p.Peek(512)
|
||||
if err != nil && err != bufio.ErrBufferFull && err != io.EOF {
|
||||
if err != nil && !errors.Is(err, bufio.ErrBufferFull) && !errors.Is(err, io.EOF) {
|
||||
return ""
|
||||
}
|
||||
return http.DetectContentType(byt)
|
||||
}
|
||||
|
||||
// copyHeader copies header values from src to dst, adding to any existing
|
||||
// values with the same header name. If keys is not empty, only those header
|
||||
// keys will be copied.
|
||||
func copyHeader(dst, src http.Header, keys ...string) {
|
||||
if len(keys) == 0 {
|
||||
for k := range src {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
for _, key := range keys {
|
||||
k := http.CanonicalHeaderKey(key)
|
||||
// copyHeader copies values for specified headers from src to dst, adding to
|
||||
// any existing values with the same header name.
|
||||
func copyHeader(dst, src http.Header, headerNames ...string) {
|
||||
for _, name := range headerNames {
|
||||
k := http.CanonicalHeaderKey(name)
|
||||
for _, v := range src[k] {
|
||||
dst.Add(k, v)
|
||||
}
|
||||
|
|
@ -284,18 +384,33 @@ func copyHeader(dst, src http.Header, keys ...string) {
|
|||
}
|
||||
|
||||
var (
|
||||
errReferrer = errors.New("request does not contain an allowed referrer")
|
||||
errDeniedHost = errors.New("request contains a denied host")
|
||||
errNotAllowed = errors.New("request does not contain an allowed host or valid signature")
|
||||
errReferrer = errors.New("request does not contain an allowed referrer")
|
||||
errDeniedHost = errors.New("request contains a denied host")
|
||||
errNotAllowed = errors.New("request does not contain an allowed host or valid signature")
|
||||
errTooManyRedirects = errors.New("too many redirects")
|
||||
errNotValid = errors.New("request is no longer valid")
|
||||
|
||||
msgNotAllowed = "requested URL is not allowed"
|
||||
msgNotAllowedInRedirect = "requested URL in redirect is not allowed"
|
||||
)
|
||||
|
||||
func (p *Proxy) now() time.Time {
|
||||
if !p.timeNow.IsZero() {
|
||||
return p.timeNow
|
||||
}
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// allowed determines whether the specified request contains an allowed
|
||||
// referrer, host, and signature. It returns an error if the request is not
|
||||
// allowed.
|
||||
// allowed or not valid any longer.
|
||||
func (p *Proxy) allowed(r *Request) error {
|
||||
if !r.Options.ValidUntil.IsZero() {
|
||||
if !p.now().Before(r.Options.ValidUntil) {
|
||||
return errNotValid
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.Referrers) > 0 && !referrerMatches(p.Referrers, r.Original) {
|
||||
return errReferrer
|
||||
}
|
||||
|
|
@ -429,7 +544,7 @@ func should304(req *http.Request, resp *http.Response) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (p *Proxy) log(v ...interface{}) {
|
||||
func (p *Proxy) log(v ...any) {
|
||||
if p.Logger != nil {
|
||||
p.Logger.Print(v...)
|
||||
} else {
|
||||
|
|
@ -437,7 +552,7 @@ func (p *Proxy) log(v ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Proxy) logf(format string, v ...interface{}) {
|
||||
func (p *Proxy) logf(format string, v ...any) {
|
||||
if p.Logger != nil {
|
||||
p.Logger.Printf(format, v...)
|
||||
} else {
|
||||
|
|
@ -458,7 +573,12 @@ type TransformingTransport struct {
|
|||
// responses are properly cached.
|
||||
CachingClient *http.Client
|
||||
|
||||
log func(format string, v ...interface{})
|
||||
// limiter limits the number of concurrent transformations being processed.
|
||||
limiter chan struct{}
|
||||
|
||||
log func(format string, v ...any)
|
||||
|
||||
updateCacheHeaders func(hdr http.Header)
|
||||
}
|
||||
|
||||
// RoundTrip implements the http.RoundTripper interface.
|
||||
|
|
@ -468,7 +588,11 @@ func (t *TransformingTransport) RoundTrip(req *http.Request) (*http.Response, er
|
|||
if t.log != nil {
|
||||
t.log("fetching remote URL: %v", req.URL)
|
||||
}
|
||||
return t.Transport.RoundTrip(req)
|
||||
resp, err := t.Transport.RoundTrip(req)
|
||||
if err == nil && t.updateCacheHeaders != nil {
|
||||
t.updateCacheHeaders(resp.Header)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
f := req.URL.Fragment
|
||||
|
|
@ -483,10 +607,26 @@ func (t *TransformingTransport) RoundTrip(req *http.Request) (*http.Response, er
|
|||
|
||||
if should304(req, resp) {
|
||||
// bare 304 response, full response will be used from cache
|
||||
return &http.Response{StatusCode: http.StatusNotModified}, nil
|
||||
return &http.Response{
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Status: fmt.Sprintf("%d %s", http.StatusNotModified, http.StatusText(http.StatusNotModified)),
|
||||
StatusCode: http.StatusNotModified,
|
||||
Body: http.NoBody,
|
||||
}, nil
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
// enforce limiter after we've checked if we can early return a 304 response,
|
||||
// but before we read the response body and perform transformations.
|
||||
if t.limiter != nil {
|
||||
t.limiter <- struct{}{}
|
||||
defer func() {
|
||||
<-t.limiter
|
||||
}()
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,13 +12,21 @@ import (
|
|||
"image"
|
||||
"image/png"
|
||||
"log"
|
||||
"maps"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/die-net/lrucache"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gregjones/httpcache"
|
||||
)
|
||||
|
||||
func TestPeekContentType(t *testing.T) {
|
||||
|
|
@ -62,30 +70,12 @@ func TestCopyHeader(t *testing.T) {
|
|||
},
|
||||
|
||||
// copy headers
|
||||
{
|
||||
dst: http.Header{},
|
||||
src: http.Header{"A": []string{"a"}},
|
||||
keys: nil,
|
||||
want: http.Header{"A": []string{"a"}},
|
||||
},
|
||||
{
|
||||
dst: http.Header{"A": []string{"a"}},
|
||||
src: http.Header{"B": []string{"b"}},
|
||||
keys: nil,
|
||||
want: http.Header{"A": []string{"a"}, "B": []string{"b"}},
|
||||
},
|
||||
{
|
||||
dst: http.Header{"A": []string{"a"}},
|
||||
src: http.Header{"B": []string{"b"}, "C": []string{"c"}},
|
||||
keys: []string{"B"},
|
||||
want: http.Header{"A": []string{"a"}, "B": []string{"b"}},
|
||||
},
|
||||
{
|
||||
dst: http.Header{"A": []string{"a1"}},
|
||||
src: http.Header{"A": []string{"a2"}},
|
||||
keys: nil,
|
||||
want: http.Header{"A": []string{"a1", "a2"}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -103,7 +93,7 @@ func TestCopyHeader(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAllowed(t *testing.T) {
|
||||
allowHosts := []string{"good"}
|
||||
good := []string{"good"}
|
||||
key := [][]byte{
|
||||
[]byte("c0ffee"),
|
||||
}
|
||||
|
|
@ -120,8 +110,11 @@ func TestAllowed(t *testing.T) {
|
|||
return req
|
||||
}
|
||||
|
||||
now := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
tests := []struct {
|
||||
url string
|
||||
now time.Time
|
||||
options Options
|
||||
allowHosts []string
|
||||
denyHosts []string
|
||||
|
|
@ -131,38 +124,43 @@ func TestAllowed(t *testing.T) {
|
|||
allowed bool
|
||||
}{
|
||||
// no allowHosts or signature key
|
||||
{"http://test/image", emptyOptions, nil, nil, nil, nil, nil, true},
|
||||
{url: "http://test/image", allowed: true},
|
||||
|
||||
// allowHosts
|
||||
{"http://good/image", emptyOptions, allowHosts, nil, nil, nil, nil, true},
|
||||
{"http://bad/image", emptyOptions, allowHosts, nil, nil, nil, nil, false},
|
||||
{url: "http://good/image", allowHosts: good, allowed: true},
|
||||
{url: "http://bad/image", allowHosts: good, allowed: false},
|
||||
|
||||
// referrer
|
||||
{"http://test/image", emptyOptions, nil, nil, allowHosts, nil, genRequest(map[string]string{"Referer": "http://good/foo"}), true},
|
||||
{"http://test/image", emptyOptions, nil, nil, allowHosts, nil, genRequest(map[string]string{"Referer": "http://bad/foo"}), false},
|
||||
{"http://test/image", emptyOptions, nil, nil, allowHosts, nil, genRequest(map[string]string{"Referer": "MALFORMED!!"}), false},
|
||||
{"http://test/image", emptyOptions, nil, nil, allowHosts, nil, genRequest(map[string]string{}), false},
|
||||
{url: "http://test/image", referrers: good, request: genRequest(map[string]string{"Referer": "http://good/foo"}), allowed: true},
|
||||
{url: "http://test/image", referrers: good, request: genRequest(map[string]string{"Referer": "http://bad/foo"}), allowed: false},
|
||||
{url: "http://test/image", referrers: good, request: genRequest(map[string]string{"Referer": "MALFORMED!!"}), allowed: false},
|
||||
{url: "http://test/image", referrers: good, request: genRequest(map[string]string{}), allowed: false},
|
||||
|
||||
// signature key
|
||||
{"http://test/image", Options{Signature: "NDx5zZHx7QfE8E-ijowRreq6CJJBZjwiRfOVk_mkfQQ="}, nil, nil, nil, key, nil, true},
|
||||
{"http://test/image", Options{Signature: "NDx5zZHx7QfE8E-ijowRreq6CJJBZjwiRfOVk_mkfQQ="}, nil, nil, nil, multipleKey, nil, true}, // signed with key "c0ffee"
|
||||
{"http://test/image", Options{Signature: "FWIawYV4SEyI4zKJMeGugM-eJM1eI_jXPEQ20ZgRe4A="}, nil, nil, nil, multipleKey, nil, true}, // signed with key "beer"
|
||||
{"http://test/image", Options{Signature: "deadbeef"}, nil, nil, nil, key, nil, false},
|
||||
{"http://test/image", Options{Signature: "deadbeef"}, nil, nil, nil, multipleKey, nil, false},
|
||||
{"http://test/image", emptyOptions, nil, nil, nil, key, nil, false},
|
||||
{url: "http://test/image", options: Options{Signature: "NDx5zZHx7QfE8E-ijowRreq6CJJBZjwiRfOVk_mkfQQ="}, keys: key, allowed: true},
|
||||
{url: "http://test/image", options: Options{Signature: "NDx5zZHx7QfE8E-ijowRreq6CJJBZjwiRfOVk_mkfQQ="}, keys: multipleKey, allowed: true}, // signed with key "c0ffee"
|
||||
{url: "http://test/image", options: Options{Signature: "FWIawYV4SEyI4zKJMeGugM-eJM1eI_jXPEQ20ZgRe4A="}, keys: multipleKey, allowed: true}, // signed with key "beer"
|
||||
{url: "http://test/image", options: Options{Signature: "deadbeef"}, keys: key, allowed: false},
|
||||
{url: "http://test/image", options: Options{Signature: "deadbeef"}, keys: multipleKey, allowed: false},
|
||||
{url: "http://test/image", keys: key, allowed: false},
|
||||
|
||||
// allowHosts and signature
|
||||
{"http://good/image", emptyOptions, allowHosts, nil, nil, key, nil, true},
|
||||
{"http://bad/image", Options{Signature: "gWivrPhXBbsYEwpmWAKjbJEiAEgZwbXbltg95O2tgNI="}, nil, nil, nil, key, nil, true},
|
||||
{"http://bad/image", emptyOptions, allowHosts, nil, nil, key, nil, false},
|
||||
{url: "http://good/image", allowHosts: good, keys: key, allowed: true},
|
||||
{url: "http://bad/image", options: Options{Signature: "gWivrPhXBbsYEwpmWAKjbJEiAEgZwbXbltg95O2tgNI="}, keys: key, allowed: true},
|
||||
{url: "http://bad/image", allowHosts: good, keys: key, allowed: false},
|
||||
|
||||
// deny requests that match denyHosts, even if signature is valid or also matches allowHosts
|
||||
{"http://test/image", emptyOptions, nil, []string{"test"}, nil, nil, nil, false},
|
||||
{"http://test:3000/image", emptyOptions, nil, []string{"test"}, nil, nil, nil, false},
|
||||
{"http://test/image", emptyOptions, []string{"test"}, []string{"test"}, nil, nil, nil, false},
|
||||
{"http://test/image", Options{Signature: "NDx5zZHx7QfE8E-ijowRreq6CJJBZjwiRfOVk_mkfQQ="}, nil, []string{"test"}, nil, key, nil, false},
|
||||
{"http://127.0.0.1/image", emptyOptions, nil, []string{"127.0.0.0/8"}, nil, nil, nil, false},
|
||||
{"http://127.0.0.1:3000/image", emptyOptions, nil, []string{"127.0.0.0/8"}, nil, nil, nil, false},
|
||||
{url: "http://test/image", denyHosts: []string{"test"}, allowed: false},
|
||||
{url: "http://test:3000/image", denyHosts: []string{"test"}, allowed: false},
|
||||
{url: "http://test/image", allowHosts: []string{"test"}, denyHosts: []string{"test"}, allowed: false},
|
||||
{url: "http://test/image", options: Options{Signature: "NDx5zZHx7QfE8E-ijowRreq6CJJBZjwiRfOVk_mkfQQ="}, denyHosts: []string{"test"}, keys: key, allowed: false},
|
||||
{url: "http://127.0.0.1/image", denyHosts: []string{"127.0.0.0/8"}, allowed: false},
|
||||
{url: "http://127.0.0.1:3000/image", denyHosts: []string{"127.0.0.0/8"}, allowed: false},
|
||||
|
||||
// valid until options
|
||||
{url: "http://test/image", now: now, options: Options{ValidUntil: now.Add(time.Second)}, allowed: true},
|
||||
{url: "http://test/image", now: now, options: Options{ValidUntil: now.Add(-time.Second)}, allowed: false},
|
||||
{url: "http://test/image", now: now, options: Options{ValidUntil: now}, allowed: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -171,6 +169,7 @@ func TestAllowed(t *testing.T) {
|
|||
p.DenyHosts = tt.denyHosts
|
||||
p.SignatureKeys = tt.keys
|
||||
p.Referrers = tt.referrers
|
||||
p.timeNow = tt.now
|
||||
|
||||
u, err := url.Parse(tt.url)
|
||||
if err != nil {
|
||||
|
|
@ -346,9 +345,11 @@ func TestShould304(t *testing.T) {
|
|||
|
||||
// testTransport is an http.RoundTripper that returns certained canned
|
||||
// responses for particular requests.
|
||||
type testTransport struct{}
|
||||
type testTransport struct {
|
||||
replyNotModified bool
|
||||
}
|
||||
|
||||
func (t testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
func (t *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
var raw string
|
||||
|
||||
switch req.URL.Path {
|
||||
|
|
@ -366,18 +367,194 @@ func (t testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
_ = png.Encode(img, m)
|
||||
|
||||
raw = fmt.Sprintf("HTTP/1.1 200 OK\nContent-Length: %d\nContent-Type: image/png\n\n%s", len(img.Bytes()), img.Bytes())
|
||||
case "/redirect-to-notmodified":
|
||||
parts := []string{
|
||||
"HTTP/1.1 303\nLocation: http://notmodified.test/notmodified?X-Security-Token=",
|
||||
uuid.NewString(),
|
||||
"#_=_\nCache-Control: no-store\n\n",
|
||||
}
|
||||
raw = strings.Join(parts, "")
|
||||
case "/notmodified":
|
||||
if t.replyNotModified {
|
||||
raw = "HTTP/1.1 304 Not modified\nEtag: \"abcdef\"\n\n"
|
||||
} else {
|
||||
raw = "HTTP/1.1 200 OK\nEtag: \"abcdef\"\n\nOriginal response\n"
|
||||
}
|
||||
default:
|
||||
raw = "HTTP/1.1 404 Not Found\n\n"
|
||||
redirectRegexp := regexp.MustCompile(`/redirects-(\d+)`)
|
||||
if redirectRegexp.MatchString(req.URL.Path) {
|
||||
redirectsLeft, _ := strconv.ParseUint(redirectRegexp.FindStringSubmatch(req.URL.Path)[1], 10, 8)
|
||||
if redirectsLeft == 0 {
|
||||
raw = "HTTP/1.1 200 OK\n\n"
|
||||
} else {
|
||||
raw = fmt.Sprintf("HTTP/1.1 302\nLocation: /http://redirect.test/redirects-%d\n\n", redirectsLeft-1)
|
||||
}
|
||||
} else {
|
||||
raw = "HTTP/1.1 404 Not Found\n\n"
|
||||
}
|
||||
}
|
||||
|
||||
buf := bufio.NewReader(bytes.NewBufferString(raw))
|
||||
return http.ReadResponse(buf, req)
|
||||
}
|
||||
|
||||
func TestProxy_UpdateCacheHeaders(t *testing.T) {
|
||||
date := "Mon, 02 Jan 2006 15:04:05 MST"
|
||||
exp := "Mon, 02 Jan 2006 16:04:05 MST"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
minDuration time.Duration
|
||||
forceCache bool
|
||||
headers http.Header
|
||||
want http.Header
|
||||
}{
|
||||
{
|
||||
name: "zero",
|
||||
headers: http.Header{},
|
||||
want: http.Header{},
|
||||
},
|
||||
{
|
||||
name: "no min duration",
|
||||
headers: http.Header{
|
||||
"Date": {date},
|
||||
"Expires": {exp},
|
||||
"Cache-Control": {"max-age=600"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Date": {date},
|
||||
"Expires": {exp},
|
||||
"Cache-Control": {"max-age=600"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "min duration, no header",
|
||||
minDuration: 30 * time.Second,
|
||||
headers: http.Header{},
|
||||
want: http.Header{
|
||||
"Cache-Control": {"max-age=30"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cache control exceeds min duration",
|
||||
minDuration: 30 * time.Second,
|
||||
headers: http.Header{
|
||||
"Cache-Control": {"max-age=600"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Cache-Control": {"max-age=600"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cache control exceeds min duration, expires",
|
||||
minDuration: 30 * time.Second,
|
||||
headers: http.Header{
|
||||
"Date": {date},
|
||||
"Expires": {exp},
|
||||
"Cache-Control": {"max-age=86400"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Date": {date},
|
||||
"Cache-Control": {"max-age=86400"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "min duration exceeds cache control",
|
||||
minDuration: 1 * time.Hour,
|
||||
headers: http.Header{
|
||||
"Cache-Control": {"max-age=600"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Cache-Control": {"max-age=3600"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "min duration exceeds cache control, expires",
|
||||
minDuration: 2 * time.Hour,
|
||||
headers: http.Header{
|
||||
"Date": {date},
|
||||
"Expires": {exp},
|
||||
"Cache-Control": {"max-age=600"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Date": {date},
|
||||
"Cache-Control": {"max-age=7200"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "expires exceeds min duration, cache control",
|
||||
minDuration: 30 * time.Minute,
|
||||
headers: http.Header{
|
||||
"Date": {date},
|
||||
"Expires": {exp},
|
||||
"Cache-Control": {"max-age=600"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Date": {date},
|
||||
"Cache-Control": {"max-age=3600"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "respect no-store",
|
||||
headers: http.Header{
|
||||
"Cache-Control": {"max-age=600, no-store"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Cache-Control": {"max-age=600, no-store"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "respect private",
|
||||
headers: http.Header{
|
||||
"Cache-Control": {"max-age=600, private"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Cache-Control": {"max-age=600, no-store, private"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "force cache, normalize directives",
|
||||
forceCache: true,
|
||||
headers: http.Header{
|
||||
"Cache-Control": {"MAX-AGE=600, no-store, private"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Cache-Control": {"max-age=600"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "force cache with min duration",
|
||||
minDuration: 1 * time.Hour,
|
||||
forceCache: true,
|
||||
headers: http.Header{
|
||||
"Cache-Control": {"max-age=600, private, no-store"},
|
||||
},
|
||||
want: http.Header{
|
||||
"Cache-Control": {"max-age=3600"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := &Proxy{
|
||||
MinimumCacheDuration: tt.minDuration,
|
||||
ForceCache: tt.forceCache,
|
||||
}
|
||||
hdr := maps.Clone(tt.headers)
|
||||
p.updateCacheHeaders(hdr)
|
||||
|
||||
if !reflect.DeepEqual(hdr, tt.want) {
|
||||
t.Errorf("updateCacheHeaders(%v) returned %v, want %v", tt.headers, hdr, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxy_ServeHTTP(t *testing.T) {
|
||||
p := &Proxy{
|
||||
Client: &http.Client{
|
||||
Transport: testTransport{},
|
||||
Transport: &testTransport{},
|
||||
},
|
||||
AllowHosts: []string{"good.test"},
|
||||
ContentTypes: []string{"image/*"},
|
||||
|
|
@ -415,7 +592,7 @@ func TestProxy_ServeHTTP(t *testing.T) {
|
|||
func TestProxy_ServeHTTP_is304(t *testing.T) {
|
||||
p := &Proxy{
|
||||
Client: &http.Client{
|
||||
Transport: testTransport{},
|
||||
Transport: &testTransport{},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -432,6 +609,81 @@ func TestProxy_ServeHTTP_is304(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProxy_ServeHTTP_cached304(t *testing.T) {
|
||||
cache := lrucache.New(1024*1024*8, 0)
|
||||
client := new(http.Client)
|
||||
tt := testTransport{}
|
||||
client.Transport = &httpcache.Transport{
|
||||
Transport: &TransformingTransport{
|
||||
Transport: &tt,
|
||||
CachingClient: client,
|
||||
},
|
||||
Cache: cache,
|
||||
MarkCachedResponses: true,
|
||||
}
|
||||
|
||||
p := &Proxy{
|
||||
Client: client,
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
// prime the cache
|
||||
req := httptest.NewRequest("GET", "http://localhost//http://good.test/redirect-to-notmodified", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
p.ServeHTTP(recorder, req)
|
||||
|
||||
resp := recorder.Result()
|
||||
if got, want := resp.StatusCode, http.StatusOK; got != want {
|
||||
t.Errorf("ServeHTTP(%v) returned status %d, want %d", req, got, want)
|
||||
}
|
||||
if _, found := cache.Get("http://good.test/redirect-to-notmodified#0x0"); !found {
|
||||
t.Errorf("Response to http://good.test/redirect-to-notmodified#0x0 should be cached")
|
||||
}
|
||||
|
||||
// now make the same request again, but this time make sure the server responds with a 304
|
||||
tt.replyNotModified = true
|
||||
req = httptest.NewRequest("GET", "http://localhost//http://good.test/redirect-to-notmodified", nil)
|
||||
recorder = httptest.NewRecorder()
|
||||
p.ServeHTTP(recorder, req)
|
||||
|
||||
resp = recorder.Result()
|
||||
if got, want := resp.StatusCode, http.StatusOK; got != want {
|
||||
t.Errorf("ServeHTTP(%v) returned status %d, want %d", req, got, want)
|
||||
}
|
||||
|
||||
if recorder.Body.String() != "Original response\n" {
|
||||
t.Errorf("Response isn't what we expected: %v", recorder.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxy_ServeHTTP_maxRedirects(t *testing.T) {
|
||||
p := &Proxy{
|
||||
Client: &http.Client{
|
||||
Transport: &testTransport{},
|
||||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
url string
|
||||
code int
|
||||
}{
|
||||
{"/http://redirect.test/redirects-0", http.StatusOK},
|
||||
{"/http://redirect.test/redirects-2", http.StatusOK},
|
||||
{"/http://redirect.test/redirects-11", http.StatusInternalServerError}, // too many redirects
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
req, _ := http.NewRequest("GET", "http://localhost"+tt.url, nil)
|
||||
resp := httptest.NewRecorder()
|
||||
p.ServeHTTP(resp, req)
|
||||
|
||||
if got, want := resp.Code, tt.code; got != want {
|
||||
t.Errorf("ServeHTTP(%v) returned status %d, want %d", req, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxy_log(t *testing.T) {
|
||||
var b strings.Builder
|
||||
|
||||
|
|
@ -481,8 +733,9 @@ func TestProxy_log_default(t *testing.T) {
|
|||
func TestTransformingTransport(t *testing.T) {
|
||||
client := new(http.Client)
|
||||
tr := &TransformingTransport{
|
||||
Transport: testTransport{},
|
||||
Transport: &testTransport{},
|
||||
CachingClient: client,
|
||||
limiter: make(chan struct{}, 1),
|
||||
}
|
||||
client.Transport = tr
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import (
|
|||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path"
|
||||
|
||||
|
|
@ -27,14 +27,14 @@ type cache struct {
|
|||
func (c *cache) Get(key string) ([]byte, bool) {
|
||||
r, err := c.object(key).NewReader(ctx)
|
||||
if err != nil {
|
||||
if err != storage.ErrObjectNotExist {
|
||||
if !errors.Is(err, storage.ErrObjectNotExist) {
|
||||
log.Printf("error reading from gcs: %v", err)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
value, err := ioutil.ReadAll(r)
|
||||
value, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
log.Printf("error reading from gcs: %v", err)
|
||||
return nil, false
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import (
|
|||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
"path"
|
||||
|
|
@ -36,13 +36,14 @@ func (c *cache) Get(key string) ([]byte, bool) {
|
|||
|
||||
resp, err := c.GetObject(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok && aerr.Code() != "NoSuchKey" {
|
||||
var aerr awserr.Error
|
||||
if errors.As(err, &aerr) && aerr.Code() != "NoSuchKey" {
|
||||
log.Printf("error fetching from s3: %v", aerr)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
value, err := ioutil.ReadAll(resp.Body)
|
||||
value, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Printf("error reading s3 response body: %v", err)
|
||||
return nil, false
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ var (
|
|||
Name: "request_duration_seconds",
|
||||
Help: "Request response times",
|
||||
})
|
||||
metricRequestsInFlight = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "http",
|
||||
Name: "requests_in_flight",
|
||||
Help: "Number of requests in flight",
|
||||
})
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -36,4 +41,5 @@ func init() {
|
|||
prometheus.MustRegister(metricServedFromCache)
|
||||
prometheus.MustRegister(metricRemoteErrors)
|
||||
prometheus.MustRegister(metricRequestDuration)
|
||||
prometheus.MustRegister(metricRequestsInFlight)
|
||||
}
|
||||
|
|
|
|||
7
third_party/httpcache/LICENSE
vendored
Normal file
7
third_party/httpcache/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Copyright © 2012 Greg Jones (greg.jones@gmail.com)
|
||||
|
||||
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.
|
||||
2
third_party/httpcache/README.md
vendored
Normal file
2
third_party/httpcache/README.md
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
httpcache is a copy of <https://github.com/gregjones/httpcache>
|
||||
with only the cache control header parsing logic.
|
||||
40
third_party/httpcache/httpcache.go
vendored
Normal file
40
third_party/httpcache/httpcache.go
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package httpcache
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CacheControl map[string]string
|
||||
|
||||
func ParseCacheControl(headers http.Header) CacheControl {
|
||||
cc := CacheControl{}
|
||||
ccHeader := headers.Get("Cache-Control")
|
||||
for _, part := range strings.Split(ccHeader, ",") {
|
||||
part = strings.Trim(part, " ")
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
if strings.ContainsRune(part, '=') {
|
||||
keyval := strings.Split(part, "=")
|
||||
cc[strings.ToLower(strings.Trim(keyval[0], " "))] = strings.Trim(keyval[1], ",")
|
||||
} else {
|
||||
cc[strings.ToLower(part)] = ""
|
||||
}
|
||||
}
|
||||
return cc
|
||||
}
|
||||
|
||||
func (cc CacheControl) String() string {
|
||||
parts := make([]string, 0, len(cc))
|
||||
for k, v := range cc {
|
||||
if v == "" {
|
||||
parts = append(parts, k)
|
||||
} else {
|
||||
parts = append(parts, k+"="+v)
|
||||
}
|
||||
}
|
||||
sort.StringSlice(parts).Sort()
|
||||
return strings.Join(parts, ", ")
|
||||
}
|
||||
67
transform.go
67
transform.go
|
|
@ -5,6 +5,7 @@ package imageproxy
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
_ "image/gif" // register gif format
|
||||
|
|
@ -43,6 +44,19 @@ func Transform(img []byte, opt Options) ([]byte, error) {
|
|||
return img, nil
|
||||
}
|
||||
|
||||
// decode image metadata
|
||||
cfg, _, err := image.DecodeConfig(bytes.NewReader(img))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// prevent pixel flooding attacks
|
||||
// accept no larger than a 100 megapixel image.
|
||||
const maxPixels = 100_000_000
|
||||
if cfg.Width*cfg.Height > maxPixels {
|
||||
return nil, errors.New("image too large")
|
||||
}
|
||||
|
||||
// decode image
|
||||
m, format, err := image.Decode(bytes.NewReader(img))
|
||||
if err != nil {
|
||||
|
|
@ -198,14 +212,8 @@ func cropParams(m image.Image, opt Options) image.Rectangle {
|
|||
}
|
||||
|
||||
// bottom right coordinate of crop
|
||||
x1 := x0 + w
|
||||
if x1 > imgW {
|
||||
x1 = imgW
|
||||
}
|
||||
y1 := y0 + h
|
||||
if y1 > imgH {
|
||||
y1 = imgH
|
||||
}
|
||||
x1 := min(x0+w, imgW)
|
||||
y1 := min(y0+h, imgH)
|
||||
|
||||
return image.Rect(x0, y0, x1, y1)
|
||||
}
|
||||
|
|
@ -267,6 +275,11 @@ func transformImage(m image.Image, opt Options) image.Image {
|
|||
timer := prometheus.NewTimer(metricTransformationDuration)
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
// trim
|
||||
if opt.Trim {
|
||||
m = trimEdges(m)
|
||||
}
|
||||
|
||||
// Parse crop and resize parameters before applying any transforms.
|
||||
// This is to ensure that any percentage-based values are based off the
|
||||
// size of the original image.
|
||||
|
|
@ -311,3 +324,41 @@ func transformImage(m image.Image, opt Options) image.Image {
|
|||
|
||||
return m
|
||||
}
|
||||
|
||||
// trimEdges returns a new image with solid color borders of the image removed.
|
||||
// The pixel at the top left corner is used to match the border color.
|
||||
func trimEdges(img image.Image) image.Image {
|
||||
bounds := img.Bounds()
|
||||
minX, minY, maxX, maxY := bounds.Max.X, bounds.Max.Y, bounds.Min.X, bounds.Min.Y
|
||||
|
||||
// Get the color of the first pixel (top-left corner)
|
||||
baseColor := img.At(bounds.Min.X, bounds.Min.Y)
|
||||
|
||||
// Check each pixel and find the bounding box of non-matching pixels
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
if img.At(x, y) != baseColor { // Non-matching pixel
|
||||
if x < minX {
|
||||
minX = x
|
||||
}
|
||||
if y < minY {
|
||||
minY = y
|
||||
}
|
||||
if x > maxX {
|
||||
maxX = x
|
||||
}
|
||||
if y > maxY {
|
||||
maxY = y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no non-matching pixels are found, return the original image
|
||||
if minX >= maxX || minY >= maxY {
|
||||
return img
|
||||
}
|
||||
|
||||
// Crop the image to the bounding box of non-matching pixels
|
||||
return imaging.Crop(img, image.Rect(minX, minY, maxX+1, maxY+1))
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue