diff options
| author | Feuerfuchs <git@feuerfuchs.dev> | 2020-05-18 12:12:43 +0200 |
|---|---|---|
| committer | Feuerfuchs <git@feuerfuchs.dev> | 2020-05-18 12:12:43 +0200 |
| commit | 4bf44b16562335b3d09b6df0150521bb5b5f776f (patch) | |
| tree | 576723e6dc9f9db48d0892f7ec354a11b973aef4 | |
| parent | Added 2 more glyphs (diff) | |
| download | gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.tar.gz gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.tar.bz2 gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.zip | |
WIP: Refactoring
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Dockerfile | 10 | ||||
| -rw-r--r-- | Makefile | 14 | ||||
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | assets/iosevka-aile-regular.woff | bin | 13588 -> 187604 bytes | |||
| -rw-r--r-- | assets/iosevka-aile-regular.woff2 | bin | 11036 -> 130736 bytes | |||
| -rw-r--r-- | assets/iosevka-term-ss03-regular.woff | bin | 13864 -> 155916 bytes | |||
| -rw-r--r-- | assets/iosevka-term-ss03-regular.woff2 | bin | 11384 -> 111644 bytes | |||
| -rw-r--r-- | assets/startpage.txt | 6 | ||||
| -rw-r--r-- | cmd/port/main.go (renamed from main.go) | 4 | ||||
| -rw-r--r-- | fonts/glyphs.txt | 2 | ||||
| -rw-r--r-- | go.mod | 13 | ||||
| -rw-r--r-- | go.sum | 217 | ||||
| -rwxr-xr-x | gopherproxy.bin | bin | 0 -> 14744336 bytes | |||
| -rw-r--r-- | gopherproxy/gopherproxy.go | 705 | ||||
| -rw-r--r-- | internal/port/gemini.go | 205 | ||||
| -rw-r--r-- | internal/port/gopher.go | 217 | ||||
| -rw-r--r-- | internal/port/main.go | 301 | ||||
| -rw-r--r-- | internal/port/tpl/gemini.html | 0 | ||||
| -rw-r--r-- | internal/port/tpl/gopher.html | 0 | ||||
| -rw-r--r-- | internal/port/tpl/startpage.html (renamed from gopherproxy/template.go) | 6 | ||||
| -rw-r--r-- | pkg/libgemini/libgemini.go (renamed from gopherproxy/libgemini/libgemini.go) | 0 | ||||
| -rw-r--r-- | pkg/libgopher/libgopher.go (renamed from gopherproxy/libgopher/libgopher.go) | 0 |
23 files changed, 971 insertions, 739 deletions
| @@ -3,6 +3,6 @@ dist | |||
| 3 | *.bak | 3 | *.bak |
| 4 | coverage.txt | 4 | coverage.txt |
| 5 | 5 | ||
| 6 | gopherproxy.bin | 6 | port.bin |
| 7 | 7 | ||
| 8 | .vscode | 8 | .vscode |
| @@ -2,16 +2,16 @@ FROM golang:alpine | |||
| 2 | 2 | ||
| 3 | EXPOSE 80/tcp | 3 | EXPOSE 80/tcp |
| 4 | 4 | ||
| 5 | ENTRYPOINT ["gopherproxy"] | 5 | ENTRYPOINT ["port"] |
| 6 | 6 | ||
| 7 | RUN \ | 7 | RUN \ |
| 8 | apk add --update git && \ | 8 | apk add --update git && \ |
| 9 | rm -rf /var/cache/apk/* | 9 | rm -rf /var/cache/apk/* |
| 10 | 10 | ||
| 11 | RUN mkdir -p /go/src/git.feuerfuchs.dev/Feuerfuchs/gopherproxy | 11 | RUN mkdir -p /go/src/git.vulpes.one/Feuerfuchs/port |
| 12 | WORKDIR /go/src/git.feuerfuchs.dev/Feuerfuchs/gopherproxy | 12 | WORKDIR /go/src/git.vulpes.one/Feuerfuchs/port |
| 13 | 13 | ||
| 14 | COPY . /go/src/git.feuerfuchs.dev/Feuerfuchs/gopherproxy | 14 | COPY . /go/src/git.vulpes.one/Feuerfuchs/port |
| 15 | 15 | ||
| 16 | RUN go get -v -d | 16 | RUN go get -v -d |
| 17 | RUN go install -v git.feuerfuchs.dev/Feuerfuchs/gopherproxy/... | 17 | RUN go install -v git.vulpes.one/Feuerfuchs/port/... |
| @@ -3,16 +3,16 @@ | |||
| 3 | all: dev | 3 | all: dev |
| 4 | 4 | ||
| 5 | dev: build | 5 | dev: build |
| 6 | ./gopherproxy.bin -bind 127.0.0.1:8000 | 6 | ./port.bin -bind 127.0.0.1:8000 |
| 7 | 7 | ||
| 8 | build: clean | 8 | build: clean |
| 9 | sassc -t compressed css/main.scss assets/style.css | 9 | sassc -t compressed css/main.scss assets/style.css |
| 10 | tsc --strict --module none --outFile /dev/stdout js/* | terser --compress --mangle -o assets/main.js -- | 10 | tsc --strict --module none --outFile /dev/stdout js/* | terser --compress --mangle -o assets/main.js -- |
| 11 | pyftsubset fonts/iosevka-term-ss03-regular.ttf --name-IDs+=0,4,6 --text-file=fonts/glyphs.txt --flavor='woff' --with-zopfli --output-file='assets/iosevka-term-ss03-regular.woff' | 11 | pyftsubset fonts/iosevka-term-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-term-ss03-regular.woff' |
| 12 | pyftsubset fonts/iosevka-term-ss03-regular.ttf --name-IDs+=0,4,6 --text-file=fonts/glyphs.txt --flavor='woff2' --output-file='assets/iosevka-term-ss03-regular.woff2' | 12 | pyftsubset fonts/iosevka-term-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-term-ss03-regular.woff2' |
| 13 | pyftsubset fonts/iosevka-aile-regular.ttf --name-IDs+=0,4,6 --text-file=fonts/glyphs.txt --flavor='woff' --with-zopfli --output-file='assets/iosevka-aile-regular.woff' | 13 | pyftsubset fonts/iosevka-aile-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-aile-regular.woff' |
| 14 | pyftsubset fonts/iosevka-aile-regular.ttf --name-IDs+=0,4,6 --text-file=fonts/glyphs.txt --flavor='woff2' --output-file='assets/iosevka-aile-regular.woff2' | 14 | pyftsubset fonts/iosevka-aile-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-aile-regular.woff2' |
| 15 | go build -o ./gopherproxy.bin ./main.go | 15 | go build -o ./port.bin ./cmd/port |
| 16 | 16 | ||
| 17 | profile: | 17 | profile: |
| 18 | @go test -cpuprofile cpu.prof -memprofile mem.prof -v -bench . | 18 | @go test -cpuprofile cpu.prof -memprofile mem.prof -v -bench . |
| @@ -24,4 +24,4 @@ test: | |||
| 24 | @go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic . | 24 | @go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic . |
| 25 | 25 | ||
| 26 | clean: | 26 | clean: |
| 27 | @git clean -f -d -X | 27 | #@git clean -f -d -X |
| @@ -1,8 +1,8 @@ | |||
| 1 | # Gopher (RFC 1436) Web Proxy | 1 | # Gopher (RFC 1436) Web Proxy |
| 2 | 2 | ||
| 3 | gopherproxy is a Gopher (RFC 1436) and Gemini (gopher://zaibatsu.circumlunar.space/1/~solderpunk/gemini/docs/) Web Proxy that acts as a gateway into Gopherspace/Geminispace by proxying standard Web HTTP requests to Gopher/Gemini requests of the target server. | 3 | port is a Gopher (RFC 1436) and Gemini (gopher://zaibatsu.circumlunar.space/1/~solderpunk/gemini/docs/) Web Proxy that acts as a gateway into Gopherspace/Geminispace by proxying standard Web HTTP requests to Gopher/Gemini requests of the target server. |
| 4 | 4 | ||
| 5 | gopherproxy is a fork of [https://github.com/prologic/gopherproxy](https://github.com/prologic/gopherproxy). | 5 | port is a fork of [https://github.com/prologic/port](https://github.com/prologic/port). |
| 6 | 6 | ||
| 7 | Demo: https://proxy.vulpes.one/ | 7 | Demo: https://proxy.vulpes.one/ |
| 8 | 8 | ||
| @@ -15,14 +15,14 @@ Demo: https://proxy.vulpes.one/ | |||
| 15 | ## Installation | 15 | ## Installation |
| 16 | 16 | ||
| 17 | ```#!bash | 17 | ```#!bash |
| 18 | $ go install git.feuerfuchs.dev/Feuerfuchs/gopherproxy/... | 18 | $ go install git.vulpes.one/Feuerfuchs/port/... |
| 19 | ``` | 19 | ``` |
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | ## Usage | 22 | ## Usage |
| 23 | 23 | ||
| 24 | ```#!bash | 24 | ```#!bash |
| 25 | $ gopherproxy | 25 | $ port |
| 26 | ``` | 26 | ``` |
| 27 | 27 | ||
| 28 | Arguments: | 28 | Arguments: |
diff --git a/assets/iosevka-aile-regular.woff b/assets/iosevka-aile-regular.woff index a23b09c..97b42ea 100644 --- a/assets/iosevka-aile-regular.woff +++ b/assets/iosevka-aile-regular.woff | |||
| Binary files differ | |||
diff --git a/assets/iosevka-aile-regular.woff2 b/assets/iosevka-aile-regular.woff2 index 2a4375e..fea3967 100644 --- a/assets/iosevka-aile-regular.woff2 +++ b/assets/iosevka-aile-regular.woff2 | |||
| Binary files differ | |||
diff --git a/assets/iosevka-term-ss03-regular.woff b/assets/iosevka-term-ss03-regular.woff index 18d4b27..eb2568a 100644 --- a/assets/iosevka-term-ss03-regular.woff +++ b/assets/iosevka-term-ss03-regular.woff | |||
| Binary files differ | |||
diff --git a/assets/iosevka-term-ss03-regular.woff2 b/assets/iosevka-term-ss03-regular.woff2 index af3e978..957b020 100644 --- a/assets/iosevka-term-ss03-regular.woff2 +++ b/assets/iosevka-term-ss03-regular.woff2 | |||
| Binary files differ | |||
diff --git a/assets/startpage.txt b/assets/startpage.txt index 53c12ab..46b22c1 100644 --- a/assets/startpage.txt +++ b/assets/startpage.txt | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | P R O X Y | 1 | P R O X Y |
| 2 | - - - - - - for - - - - - - | 2 | - - - - - - for - - - - - - |
| 3 | GOPHER + GEMINI | 3 | G O P H E R + G E M I N I |
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | GETTING STARTED -- | 6 | GETTING STARTED -- |
diff --git a/main.go b/cmd/port/main.go index f5a82fe..6cdccc6 100644 --- a/main.go +++ b/cmd/port/main.go | |||
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | "flag" | 4 | "flag" |
| 5 | "log" | 5 | "log" |
| 6 | 6 | ||
| 7 | "git.feuerfuchs.dev/Feuerfuchs/gopherproxy/gopherproxy" | 7 | port "git.vulpes.one/Feuerfuchs/port/internal/port" |
| 8 | ) | 8 | ) |
| 9 | 9 | ||
| 10 | var ( | 10 | var ( |
| @@ -21,5 +21,5 @@ func main() { | |||
| 21 | flag.Parse() | 21 | flag.Parse() |
| 22 | 22 | ||
| 23 | // Use a config struct | 23 | // Use a config struct |
| 24 | log.Fatal(gopherproxy.ListenAndServe(*bind, *startpagefile, *robotsfile, *robotsdebug, *vipsconcurrency)) | 24 | log.Fatal(port.ListenAndServe(*bind, *startpagefile, *robotsfile, *robotsdebug, *vipsconcurrency)) |
| 25 | } | 25 | } |
diff --git a/fonts/glyphs.txt b/fonts/glyphs.txt index 5acadec..ac0461a 100644 --- a/fonts/glyphs.txt +++ b/fonts/glyphs.txt | |||
| @@ -1 +1 @@ | |||
| !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`´abcdefghijklmnopqrstuvwxyz{|}~äöüÄÖÜßẞ↓↙←↖↑↗→↘€»«„“”·…°’‾█▓▒░ ▀▄‐╭╮─│╰╯╱╲╳ʻ‘ | !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`´abcdefghijklmnopqrstuvwxyz{|}~äöüÄÖÜßẞ↓↙←↖↑↗→↘€»«„“”·…°’‾█▓▒░ ▀▄‐╭╮─│╰╯┌┐└┘├╱╲╳ʻ‘ | ||
| @@ -1,13 +1,12 @@ | |||
| 1 | module git.feuerfuchs.dev/Feuerfuchs/gopherproxy | 1 | module git.vulpes.one/Feuerfuchs/port |
| 2 | 2 | ||
| 3 | require ( | 3 | require ( |
| 4 | github.com/NYTimes/gziphandler v1.1.1 | 4 | github.com/NYTimes/gziphandler v1.1.1 |
| 5 | github.com/davidbyttow/govips v0.0.0-20190304175058-d272f04c0fea | 5 | github.com/davidbyttow/govips v0.0.0-20200412130214-cbefdd8c639a |
| 6 | github.com/gobuffalo/packr/v2 v2.1.0 | 6 | github.com/gobuffalo/packr/v2 v2.8.0 |
| 7 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 | 7 | github.com/temoto/robotstxt v1.1.1 |
| 8 | github.com/temoto/robotstxt v0.0.0-20180810133444-97ee4a9ee6ea | 8 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120 |
| 9 | golang.org/x/net v0.0.0-20190311183353-d8887717615a | 9 | golang.org/x/text v0.3.2 |
| 10 | golang.org/x/text v0.3.0 | ||
| 11 | ) | 10 | ) |
| 12 | 11 | ||
| 13 | go 1.13 | 12 | go 1.13 |
| @@ -1,92 +1,309 @@ | |||
| 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||
| 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||
| 1 | github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= | 3 | github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= |
| 2 | github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= | 4 | github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= |
| 5 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||
| 6 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||
| 7 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||
| 8 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||
| 9 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||
| 10 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||
| 11 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= | ||
| 12 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | ||
| 13 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= | ||
| 14 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= | ||
| 15 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= | ||
| 16 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= | ||
| 17 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | ||
| 18 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= | ||
| 19 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= | ||
| 20 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | ||
| 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 21 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
| 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | 22 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
| 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 23 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
| 6 | github.com/davidbyttow/govips v0.0.0-20190304175058-d272f04c0fea h1:ZtETbJTO1R3qVLdVbpjrDhD5fR8bYVhhq2RMi7rOlH4= | 24 | github.com/davidbyttow/govips v0.0.0-20190304175058-d272f04c0fea h1:ZtETbJTO1R3qVLdVbpjrDhD5fR8bYVhhq2RMi7rOlH4= |
| 7 | github.com/davidbyttow/govips v0.0.0-20190304175058-d272f04c0fea/go.mod h1:a3qO525EPfJNYa0NXBcNtXzJvyQsJAxphEDa7OOHPBk= | 25 | github.com/davidbyttow/govips v0.0.0-20190304175058-d272f04c0fea/go.mod h1:a3qO525EPfJNYa0NXBcNtXzJvyQsJAxphEDa7OOHPBk= |
| 26 | github.com/davidbyttow/govips v0.0.0-20200412130214-cbefdd8c639a h1:g1X1c43wACmPtvFo+szBy/0coLkRwVJ0+i2f6YJITto= | ||
| 27 | github.com/davidbyttow/govips v0.0.0-20200412130214-cbefdd8c639a/go.mod h1:a3qO525EPfJNYa0NXBcNtXzJvyQsJAxphEDa7OOHPBk= | ||
| 28 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | ||
| 29 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= | ||
| 30 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= | ||
| 31 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | ||
| 32 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= | ||
| 33 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= | ||
| 34 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= | ||
| 35 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= | ||
| 36 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= | ||
| 8 | github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= | 37 | github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= |
| 38 | github.com/gobuffalo/attrs v1.0.0 h1:aAK8D55L0GNVbVRlaCwvAGE2BcGdEoVoK86vgQpuUZ8= | ||
| 39 | github.com/gobuffalo/attrs v1.0.0/go.mod h1:YWU+sjOr7V05rNzvCoVc5qQTC2IgEz9434TFW5GhNaw= | ||
| 9 | github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= | 40 | github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= |
| 41 | github.com/gobuffalo/depgen v0.2.0 h1:CYuqsR8sq+L9G9+A6uUcTEuaK8AGenAjtYOm238fN3M= | ||
| 42 | github.com/gobuffalo/depgen v0.2.0/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= | ||
| 10 | github.com/gobuffalo/envy v1.6.15 h1:OsV5vOpHYUpP7ZLS6sem1y40/lNX1BZj+ynMiRi21lQ= | 43 | github.com/gobuffalo/envy v1.6.15 h1:OsV5vOpHYUpP7ZLS6sem1y40/lNX1BZj+ynMiRi21lQ= |
| 11 | github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= | 44 | github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= |
| 12 | github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= | 45 | github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= |
| 13 | github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= | 46 | github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= |
| 47 | github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= | ||
| 48 | github.com/gobuffalo/envy v1.8.1 h1:RUr68liRvs0TS1D5qdW3mQv2SjAsu1QWMCx1tG4kDjs= | ||
| 49 | github.com/gobuffalo/envy v1.8.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= | ||
| 14 | github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= | 50 | github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= |
| 15 | github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= | 51 | github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= |
| 52 | github.com/gobuffalo/flect v0.2.0 h1:EWCvMGGxOjsgwlWaP+f4+Hh6yrrte7JeFL2S6b+0hdM= | ||
| 53 | github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= | ||
| 16 | github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= | 54 | github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= |
| 17 | github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e h1:JbHBQOMhE0wmpSuejnSkdnL2rULqQTwEGgVe85o7+No= | 55 | github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e h1:JbHBQOMhE0wmpSuejnSkdnL2rULqQTwEGgVe85o7+No= |
| 18 | github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= | 56 | github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= |
| 57 | github.com/gobuffalo/genny v0.6.0 h1:d7c6d66ZrTHHty01hDX1/TcTWvAJQxRZl885KWX5kHY= | ||
| 58 | github.com/gobuffalo/genny v0.6.0/go.mod h1:Vigx9VDiNscYpa/LwrURqGXLSIbzTfapt9+K6gF1kTA= | ||
| 19 | github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= | 59 | github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= |
| 60 | github.com/gobuffalo/github_flavored_markdown v1.1.0/go.mod h1:TSpTKWcRTI0+v7W3x8dkSKMLJSUpuVitlptCkpeY8ic= | ||
| 20 | github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5 h1:f3Fpd5AqsFuTHUEhUeEMIFJkX8FpVnzdW+GpYxIyXkA= | 61 | github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5 h1:f3Fpd5AqsFuTHUEhUeEMIFJkX8FpVnzdW+GpYxIyXkA= |
| 21 | github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= | 62 | github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= |
| 63 | github.com/gobuffalo/gogen v0.2.0 h1:Xx7NCe+/y++eII2aWAFZ09/81MhDCsZwvMzIFJoQRnU= | ||
| 64 | github.com/gobuffalo/gogen v0.2.0/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= | ||
| 65 | github.com/gobuffalo/helpers v0.5.0/go.mod h1:stpgxJ2C7T99NLyAxGUnYMM2zAtBk5NKQR0SIbd05j4= | ||
| 66 | github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= | ||
| 22 | github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2 h1:8thhT+kUJMTMy3HlX4+y9Da+BNJck+p109tqqKp7WDs= | 67 | github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2 h1:8thhT+kUJMTMy3HlX4+y9Da+BNJck+p109tqqKp7WDs= |
| 23 | github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= | 68 | github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= |
| 69 | github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= | ||
| 70 | github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc= | ||
| 71 | github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= | ||
| 24 | github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= | 72 | github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= |
| 25 | github.com/gobuffalo/mapi v1.0.2 h1:fq9WcL1BYrm36SzK6+aAnZ8hcp+SrmnDyAxhNx8dvJk= | 73 | github.com/gobuffalo/mapi v1.0.2 h1:fq9WcL1BYrm36SzK6+aAnZ8hcp+SrmnDyAxhNx8dvJk= |
| 26 | github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= | 74 | github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= |
| 75 | github.com/gobuffalo/mapi v1.2.1 h1:TyfbtQaW7GvS4DXdF1KQOSGrW6L0uiFmGDz+JgEIMbM= | ||
| 76 | github.com/gobuffalo/mapi v1.2.1/go.mod h1:giGJ2AUESRepOFYAzWpq8Gf/s/QDryxoEHisQtFN3cY= | ||
| 27 | github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0 h1:P6naWPiHm/7R3eYx/ub3VhaW9G+1xAMJ6vzACePaGPI= | 77 | github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0 h1:P6naWPiHm/7R3eYx/ub3VhaW9G+1xAMJ6vzACePaGPI= |
| 28 | github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= | 78 | github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= |
| 79 | github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= | ||
| 80 | github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= | ||
| 81 | github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= | ||
| 29 | github.com/gobuffalo/packr v1.25.0 h1:NtPK45yOKFdTKHTvRGKL+UIKAKmJVWIVJOZBDI/qEdY= | 82 | github.com/gobuffalo/packr v1.25.0 h1:NtPK45yOKFdTKHTvRGKL+UIKAKmJVWIVJOZBDI/qEdY= |
| 30 | github.com/gobuffalo/packr v1.25.0/go.mod h1:NqsGg8CSB2ZD+6RBIRs18G7aZqdYDlYNNvsSqP6T4/U= | 83 | github.com/gobuffalo/packr v1.25.0/go.mod h1:NqsGg8CSB2ZD+6RBIRs18G7aZqdYDlYNNvsSqP6T4/U= |
| 31 | github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= | 84 | github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= |
| 32 | github.com/gobuffalo/packr/v2 v2.1.0 h1:nWGTgGtZrR4yBQvmAKF4AthraObjRMzx6lJa9e+JbLQ= | 85 | github.com/gobuffalo/packr/v2 v2.1.0 h1:nWGTgGtZrR4yBQvmAKF4AthraObjRMzx6lJa9e+JbLQ= |
| 33 | github.com/gobuffalo/packr/v2 v2.1.0/go.mod h1:n90ZuXIc2KN2vFAOQascnPItp9A2g9QYSvYvS3AjQEM= | 86 | github.com/gobuffalo/packr/v2 v2.1.0/go.mod h1:n90ZuXIc2KN2vFAOQascnPItp9A2g9QYSvYvS3AjQEM= |
| 87 | github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= | ||
| 88 | github.com/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zNVIY= | ||
| 89 | github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g= | ||
| 90 | github.com/gobuffalo/plush v3.8.3+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI= | ||
| 34 | github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4= | 91 | github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4= |
| 35 | github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= | 92 | github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= |
| 93 | github.com/gobuffalo/syncx v0.1.0 h1://CNTQ/+VFQizkW24DrBtTBvj8c2+chz5Y7kbboQ2qk= | ||
| 94 | github.com/gobuffalo/syncx v0.1.0/go.mod h1:Mg/s+5pv7IgxEp6sA+NFpqS4o2x+R9dQNwbwT0iuOGQ= | ||
| 95 | github.com/gobuffalo/tags v2.1.7+incompatible/go.mod h1:9XmhOkyaB7UzvuY4UoZO4s67q8/xRMVJEaakauVQYeY= | ||
| 96 | github.com/gobuffalo/uuid v2.0.5+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw4cVi1RtSpnwYrxuvkfE= | ||
| 97 | github.com/gobuffalo/validate v2.0.3+incompatible/go.mod h1:N+EtDe0J8252BgfzQUChBgfd6L93m9weay53EWFVsMM= | ||
| 98 | github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= | ||
| 99 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= | ||
| 100 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= | ||
| 101 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||
| 102 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||
| 103 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||
| 104 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||
| 105 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||
| 106 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||
| 107 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||
| 108 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||
| 109 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= | ||
| 110 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= | ||
| 111 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= | ||
| 112 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= | ||
| 113 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | ||
| 36 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= | 114 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= |
| 37 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= | 115 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= |
| 38 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= | 116 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= |
| 117 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= | ||
| 118 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= | ||
| 39 | github.com/karrick/godirwalk v1.8.0 h1:ycpSqVon/QJJoaT1t8sae0tp1Stg21j+dyuS7OoagcA= | 119 | github.com/karrick/godirwalk v1.8.0 h1:ycpSqVon/QJJoaT1t8sae0tp1Stg21j+dyuS7OoagcA= |
| 40 | github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= | 120 | github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= |
| 121 | github.com/karrick/godirwalk v1.15.3 h1:0a2pXOgtB16CqIqXTiT7+K9L73f74n/aNQUnH6Ortew= | ||
| 122 | github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= | ||
| 123 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= | ||
| 124 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||
| 41 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | 125 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= |
| 126 | github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= | ||
| 127 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||
| 128 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= | ||
| 42 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | 129 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= |
| 43 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | 130 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= |
| 44 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | 131 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= |
| 132 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= | ||
| 133 | github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= | ||
| 134 | github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= | ||
| 45 | github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k= | 135 | github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k= |
| 46 | github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= | 136 | github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= |
| 137 | github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= | ||
| 138 | github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= | ||
| 47 | github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= | 139 | github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= |
| 48 | github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= | 140 | github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= |
| 141 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= | ||
| 142 | github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= | ||
| 143 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | ||
| 144 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | ||
| 145 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= | ||
| 49 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= | 146 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= |
| 50 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= | 147 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= |
| 148 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= | ||
| 149 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | ||
| 150 | github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | ||
| 151 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= | ||
| 152 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | ||
| 51 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | 153 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
| 52 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= | 154 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= |
| 53 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | 155 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
| 156 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||
| 157 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
| 54 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | 158 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
| 55 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | 159 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
| 56 | github.com/prologic/go-gopher v0.0.0-20181230133552-0c68ed5f58b0 h1:10LO/S8HVjIuEHsHea//Cena1Ztgy23f/e8HFC0w5ow= | 160 | github.com/prologic/go-gopher v0.0.0-20181230133552-0c68ed5f58b0 h1:10LO/S8HVjIuEHsHea//Cena1Ztgy23f/e8HFC0w5ow= |
| 57 | github.com/prologic/go-gopher v0.0.0-20181230133552-0c68ed5f58b0/go.mod h1:LiuwIXz4es4YIUOD6yRv8mES9n9dFbe4z0+TcrLkhXg= | 161 | github.com/prologic/go-gopher v0.0.0-20181230133552-0c68ed5f58b0/go.mod h1:LiuwIXz4es4YIUOD6yRv8mES9n9dFbe4z0+TcrLkhXg= |
| 162 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= | ||
| 163 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= | ||
| 164 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | ||
| 165 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||
| 166 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= | ||
| 167 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | ||
| 168 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= | ||
| 169 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= | ||
| 170 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= | ||
| 171 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | ||
| 58 | github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | 172 | github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= |
| 59 | github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | 173 | github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= |
| 60 | github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= | 174 | github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= |
| 61 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | 175 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= |
| 176 | github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= | ||
| 177 | github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= | ||
| 178 | github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= | ||
| 179 | github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= | ||
| 180 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | ||
| 181 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
| 182 | github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= | ||
| 183 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= | ||
| 184 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | ||
| 185 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | ||
| 62 | github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | 186 | github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= |
| 63 | github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= | 187 | github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= |
| 64 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= | 188 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= |
| 189 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= | ||
| 190 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= | ||
| 191 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= | ||
| 192 | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= | ||
| 193 | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= | ||
| 194 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= | ||
| 195 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= | ||
| 196 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= | ||
| 65 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= | 197 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= |
| 198 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= | ||
| 199 | github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= | ||
| 200 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= | ||
| 66 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= | 201 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= |
| 202 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= | ||
| 203 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= | ||
| 67 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | 204 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
| 68 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | 205 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
| 69 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | 206 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
| 70 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= | 207 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= |
| 71 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | 208 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
| 209 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||
| 210 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||
| 72 | github.com/temoto/robotstxt v0.0.0-20180810133444-97ee4a9ee6ea h1:hH8P1IiDpzRU6ZDbDh/RDnVuezi2oOXJpApa06M0zyI= | 211 | github.com/temoto/robotstxt v0.0.0-20180810133444-97ee4a9ee6ea h1:hH8P1IiDpzRU6ZDbDh/RDnVuezi2oOXJpApa06M0zyI= |
| 73 | github.com/temoto/robotstxt v0.0.0-20180810133444-97ee4a9ee6ea/go.mod h1:aOux3gHPCftJ3KHq6Pz/AlDjYJ7Y+yKfm1gU/3B0u04= | 212 | github.com/temoto/robotstxt v0.0.0-20180810133444-97ee4a9ee6ea/go.mod h1:aOux3gHPCftJ3KHq6Pz/AlDjYJ7Y+yKfm1gU/3B0u04= |
| 213 | github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++JaA= | ||
| 214 | github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= | ||
| 215 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= | ||
| 216 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= | ||
| 217 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= | ||
| 218 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= | ||
| 219 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= | ||
| 220 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= | ||
| 221 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | ||
| 222 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= | ||
| 223 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= | ||
| 74 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | 224 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |
| 225 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||
| 75 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= | 226 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= |
| 76 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | 227 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
| 228 | golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||
| 229 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||
| 230 | golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4= | ||
| 231 | golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||
| 232 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||
| 233 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||
| 234 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||
| 235 | golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= | ||
| 236 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||
| 237 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||
| 238 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||
| 239 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||
| 77 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis= | 240 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis= |
| 78 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | 241 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
| 79 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= | 242 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= |
| 80 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | 243 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
| 244 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
| 245 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||
| 246 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
| 247 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
| 248 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
| 249 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= | ||
| 250 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||
| 251 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||
| 252 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
| 253 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
| 254 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
| 81 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | 255 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
| 82 | golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | 256 | golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
| 257 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
| 258 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
| 259 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
| 83 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | 260 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
| 261 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
| 262 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
| 263 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
| 264 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
| 84 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= | 265 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= |
| 85 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | 266 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
| 267 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
| 268 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
| 269 | golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
| 270 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= | ||
| 271 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
| 86 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | 272 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= |
| 87 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | 273 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
| 274 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | ||
| 275 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||
| 276 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||
| 277 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
| 278 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
| 279 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
| 280 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||
| 88 | golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | 281 | golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
| 89 | golang.org/x/tools v0.0.0-20190404132500-923d25813098 h1:MtqjsZmyGRgMmLUgxnmMJ6RYdvd2ib8ipiayHhqSxs4= | 282 | golang.org/x/tools v0.0.0-20190404132500-923d25813098 h1:MtqjsZmyGRgMmLUgxnmMJ6RYdvd2ib8ipiayHhqSxs4= |
| 90 | golang.org/x/tools v0.0.0-20190404132500-923d25813098/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | 283 | golang.org/x/tools v0.0.0-20190404132500-923d25813098/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
| 284 | golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||
| 285 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||
| 286 | golang.org/x/tools v0.0.0-20191224055732-dd894d0a8a40/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||
| 287 | golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE= | ||
| 288 | golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= | ||
| 289 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
| 290 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
| 291 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||
| 292 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
| 293 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||
| 294 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||
| 295 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||
| 296 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | ||
| 297 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | ||
| 298 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
| 91 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 299 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
| 92 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | 300 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= |
| 301 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||
| 302 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= | ||
| 303 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | ||
| 304 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= | ||
| 305 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||
| 306 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||
| 307 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||
| 308 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||
| 309 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||
diff --git a/gopherproxy.bin b/gopherproxy.bin new file mode 100755 index 0000000..e8f94c1 --- /dev/null +++ b/gopherproxy.bin | |||
| Binary files differ | |||
diff --git a/gopherproxy/gopherproxy.go b/gopherproxy/gopherproxy.go deleted file mode 100644 index 453ee9c..0000000 --- a/gopherproxy/gopherproxy.go +++ /dev/null | |||
| @@ -1,705 +0,0 @@ | |||
| 1 | package gopherproxy | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bufio" | ||
| 5 | "bytes" | ||
| 6 | "crypto/md5" | ||
| 7 | "fmt" | ||
| 8 | "html" | ||
| 9 | "html/template" | ||
| 10 | "io" | ||
| 11 | "io/ioutil" | ||
| 12 | "log" | ||
| 13 | "mime" | ||
| 14 | "net" | ||
| 15 | "net/http" | ||
| 16 | "net/url" | ||
| 17 | "regexp" | ||
| 18 | "strings" | ||
| 19 | |||
| 20 | "golang.org/x/net/html/charset" | ||
| 21 | "golang.org/x/text/transform" | ||
| 22 | |||
| 23 | "git.feuerfuchs.dev/Feuerfuchs/gopherproxy/gopherproxy/libgemini" | ||
| 24 | "git.feuerfuchs.dev/Feuerfuchs/gopherproxy/gopherproxy/libgopher" | ||
| 25 | |||
| 26 | "github.com/NYTimes/gziphandler" | ||
| 27 | "github.com/davidbyttow/govips/pkg/vips" | ||
| 28 | "github.com/gobuffalo/packr/v2" | ||
| 29 | "github.com/temoto/robotstxt" | ||
| 30 | ) | ||
| 31 | |||
| 32 | var ( | ||
| 33 | TermEscapeSGRPattern = regexp.MustCompile("\\[\\d+(;\\d+)*m") | ||
| 34 | ) | ||
| 35 | |||
| 36 | const ( | ||
| 37 | ITEM_TYPE_GEMINI_LINE = "" | ||
| 38 | ITEM_TYPE_GEMINI_LINK = " =>" | ||
| 39 | ) | ||
| 40 | |||
| 41 | type Item struct { | ||
| 42 | Link template.URL | ||
| 43 | Type string | ||
| 44 | Text string | ||
| 45 | } | ||
| 46 | |||
| 47 | type AssetList struct { | ||
| 48 | Style string | ||
| 49 | JS string | ||
| 50 | FontW string | ||
| 51 | FontW2 string | ||
| 52 | PropFontW string | ||
| 53 | PropFontW2 string | ||
| 54 | } | ||
| 55 | |||
| 56 | type TemplateVariables struct { | ||
| 57 | Title string | ||
| 58 | URI string | ||
| 59 | Assets AssetList | ||
| 60 | RawText string | ||
| 61 | Lines []Item | ||
| 62 | Error bool | ||
| 63 | Protocol string | ||
| 64 | } | ||
| 65 | |||
| 66 | func resolveURI(uri string, baseURL *url.URL) (resolvedURI string) { | ||
| 67 | if strings.HasPrefix(uri, "//") { | ||
| 68 | resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "//") | ||
| 69 | } else if strings.HasPrefix(uri, "gemini://") { | ||
| 70 | resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "gemini://") | ||
| 71 | } else if strings.HasPrefix(uri, "gopher://") { | ||
| 72 | resolvedURI = "/gopher/" + strings.TrimPrefix(uri, "gopher://") | ||
| 73 | } else { | ||
| 74 | url, err := url.Parse(uri) | ||
| 75 | if err != nil { | ||
| 76 | return "" | ||
| 77 | } | ||
| 78 | adjustedURI := baseURL.ResolveReference(url) | ||
| 79 | path := adjustedURI.Path | ||
| 80 | if !strings.HasPrefix(path, "/") { | ||
| 81 | path = "/" + path | ||
| 82 | } | ||
| 83 | if adjustedURI.Scheme == "gemini" { | ||
| 84 | resolvedURI = "/gemini/" + adjustedURI.Host + path | ||
| 85 | } else if adjustedURI.Scheme == "gopher" { | ||
| 86 | resolvedURI = "/gopher/" + adjustedURI.Host + path | ||
| 87 | } else { | ||
| 88 | resolvedURI = adjustedURI.String() | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | return | ||
| 93 | } | ||
| 94 | |||
| 95 | func renderGopherDirectory(w http.ResponseWriter, tpl *template.Template, assetList AssetList, uri string, hostport string, d libgopher.Directory) error { | ||
| 96 | var title string | ||
| 97 | |||
| 98 | out := make([]Item, len(d.Items)) | ||
| 99 | |||
| 100 | for i, x := range d.Items { | ||
| 101 | if x.Type == libgopher.INFO && x.Selector == "TITLE" { | ||
| 102 | title = x.Description | ||
| 103 | continue | ||
| 104 | } | ||
| 105 | |||
| 106 | tr := Item{ | ||
| 107 | Text: x.Description, | ||
| 108 | Type: x.Type.String(), | ||
| 109 | } | ||
| 110 | |||
| 111 | if x.Type == libgopher.INFO { | ||
| 112 | out[i] = tr | ||
| 113 | continue | ||
| 114 | } | ||
| 115 | |||
| 116 | if strings.HasPrefix(x.Selector, "URL:") || strings.HasPrefix(x.Selector, "/URL:") { | ||
| 117 | link := strings.TrimPrefix(strings.TrimPrefix(x.Selector, "/"), "URL:") | ||
| 118 | if strings.HasPrefix(link, "gemini://") { | ||
| 119 | link = fmt.Sprintf( | ||
| 120 | "/gemini/%s", | ||
| 121 | strings.TrimPrefix(link, "gemini://"), | ||
| 122 | ) | ||
| 123 | } else if strings.HasPrefix(link, "gopher://") { | ||
| 124 | link = fmt.Sprintf( | ||
| 125 | "/gopher/%s", | ||
| 126 | strings.TrimPrefix(link, "gopher://"), | ||
| 127 | ) | ||
| 128 | } | ||
| 129 | tr.Link = template.URL(link) | ||
| 130 | } else { | ||
| 131 | var linkHostport string | ||
| 132 | if x.Port != "70" { | ||
| 133 | linkHostport = net.JoinHostPort(x.Host, x.Port) | ||
| 134 | } else { | ||
| 135 | linkHostport = x.Host | ||
| 136 | } | ||
| 137 | |||
| 138 | path := url.PathEscape(x.Selector) | ||
| 139 | path = strings.Replace(path, "%2F", "/", -1) | ||
| 140 | tr.Link = template.URL( | ||
| 141 | fmt.Sprintf( | ||
| 142 | "/gopher/%s/%s%s", | ||
| 143 | linkHostport, | ||
| 144 | string(byte(x.Type)), | ||
| 145 | path, | ||
| 146 | ), | ||
| 147 | ) | ||
| 148 | } | ||
| 149 | |||
| 150 | out[i] = tr | ||
| 151 | } | ||
| 152 | |||
| 153 | if title == "" { | ||
| 154 | if uri != "" { | ||
| 155 | title = fmt.Sprintf("%s/%s", hostport, uri) | ||
| 156 | } else { | ||
| 157 | title = hostport | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | return tpl.Execute(w, TemplateVariables{ | ||
| 162 | Title: title, | ||
| 163 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 164 | Assets: assetList, | ||
| 165 | Lines: out, | ||
| 166 | Protocol: "gopher", | ||
| 167 | }) | ||
| 168 | } | ||
| 169 | |||
| 170 | func parseGeminiDocument(body *bytes.Buffer, uri string, hostport string) (items []Item) { | ||
| 171 | baseURL, err := url.Parse(fmt.Sprintf( | ||
| 172 | "gemini://%s/%s", | ||
| 173 | hostport, | ||
| 174 | uri, | ||
| 175 | )) | ||
| 176 | if err != nil { | ||
| 177 | return []Item{} | ||
| 178 | } | ||
| 179 | |||
| 180 | scanner := bufio.NewScanner(body) | ||
| 181 | |||
| 182 | for scanner.Scan() { | ||
| 183 | line := strings.Trim(scanner.Text(), "\r\n") | ||
| 184 | line = TermEscapeSGRPattern.ReplaceAllString(line, "") | ||
| 185 | |||
| 186 | item := Item{ | ||
| 187 | Type: ITEM_TYPE_GEMINI_LINE, | ||
| 188 | Text: line, | ||
| 189 | } | ||
| 190 | |||
| 191 | linkMatch := libgemini.LinkPattern.FindStringSubmatch(line) | ||
| 192 | if len(linkMatch) != 0 && linkMatch[0] != "" { | ||
| 193 | item.Type = ITEM_TYPE_GEMINI_LINK | ||
| 194 | item.Link = template.URL(resolveURI(linkMatch[1], baseURL)) | ||
| 195 | item.Text = linkMatch[2] | ||
| 196 | if item.Text == "" { | ||
| 197 | item.Text = linkMatch[1] | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | items = append(items, item) | ||
| 202 | } | ||
| 203 | |||
| 204 | return | ||
| 205 | } | ||
| 206 | |||
| 207 | func DefaultHandler(tpl *template.Template, startpagetext string, assetList AssetList) http.HandlerFunc { | ||
| 208 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 209 | if err := tpl.Execute(w, TemplateVariables{ | ||
| 210 | Title: "Gopher/Gemini proxy", | ||
| 211 | Assets: assetList, | ||
| 212 | RawText: startpagetext, | ||
| 213 | Protocol: "startpage", | ||
| 214 | }); err != nil { | ||
| 215 | log.Println("Template error: " + err.Error()) | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | // GopherHandler returns a Handler that proxies requests | ||
| 221 | // to the specified Gopher server as denoated by the first argument | ||
| 222 | // to the request path and renders the content using the provided template. | ||
| 223 | // The optional robots parameters points to a robotstxt.RobotsData struct | ||
| 224 | // to test user agents against a configurable robotst.txt file. | ||
| 225 | func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { | ||
| 226 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 227 | agent := req.UserAgent() | ||
| 228 | path := strings.TrimPrefix(req.URL.Path, "/gopher/") | ||
| 229 | |||
| 230 | if robotsdata != nil && robotsdebug && !robotsdata.TestAgent(path, agent) { | ||
| 231 | log.Printf("UserAgent %s ignored robots.txt", agent) | ||
| 232 | } | ||
| 233 | |||
| 234 | parts := strings.Split(path, "/") | ||
| 235 | hostport := parts[0] | ||
| 236 | |||
| 237 | if len(hostport) == 0 { | ||
| 238 | http.Redirect(w, req, "/", http.StatusFound) | ||
| 239 | return | ||
| 240 | } | ||
| 241 | |||
| 242 | title := hostport | ||
| 243 | |||
| 244 | var qs string | ||
| 245 | |||
| 246 | if req.URL.RawQuery != "" { | ||
| 247 | qs = fmt.Sprintf("?%s", url.QueryEscape(req.URL.RawQuery)) | ||
| 248 | } | ||
| 249 | |||
| 250 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) | ||
| 251 | if err != nil { | ||
| 252 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 253 | Title: title, | ||
| 254 | URI: hostport, | ||
| 255 | Assets: assetList, | ||
| 256 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 257 | Error: true, | ||
| 258 | Protocol: "gopher", | ||
| 259 | }); e != nil { | ||
| 260 | log.Println("Template error: " + e.Error()) | ||
| 261 | log.Println(err.Error()) | ||
| 262 | } | ||
| 263 | return | ||
| 264 | } | ||
| 265 | |||
| 266 | if uri != "" { | ||
| 267 | title = fmt.Sprintf("%s/%s", hostport, uri) | ||
| 268 | } | ||
| 269 | |||
| 270 | res, err := libgopher.Get( | ||
| 271 | fmt.Sprintf( | ||
| 272 | "gopher://%s/%s%s", | ||
| 273 | hostport, | ||
| 274 | uri, | ||
| 275 | qs, | ||
| 276 | ), | ||
| 277 | ) | ||
| 278 | |||
| 279 | if err != nil { | ||
| 280 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 281 | Title: title, | ||
| 282 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 283 | Assets: assetList, | ||
| 284 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 285 | Error: true, | ||
| 286 | Protocol: "gopher", | ||
| 287 | }); e != nil { | ||
| 288 | log.Println("Template error: " + e.Error()) | ||
| 289 | } | ||
| 290 | return | ||
| 291 | } | ||
| 292 | |||
| 293 | if res.Body != nil { | ||
| 294 | if len(parts) < 2 { | ||
| 295 | io.Copy(w, res.Body) | ||
| 296 | } else if strings.HasPrefix(parts[1], "0") && !strings.HasSuffix(uri, ".xml") && !strings.HasSuffix(uri, ".asc") { | ||
| 297 | buf := new(bytes.Buffer) | ||
| 298 | buf.ReadFrom(res.Body) | ||
| 299 | |||
| 300 | if err := tpl.Execute(w, TemplateVariables{ | ||
| 301 | Title: title, | ||
| 302 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 303 | Assets: assetList, | ||
| 304 | RawText: buf.String(), | ||
| 305 | Protocol: "gopher", | ||
| 306 | }); err != nil { | ||
| 307 | log.Println("Template error: " + err.Error()) | ||
| 308 | } | ||
| 309 | } else if strings.HasPrefix(parts[1], "T") { | ||
| 310 | _, _, err = vips.NewTransform(). | ||
| 311 | Load(res.Body). | ||
| 312 | ResizeStrategy(vips.ResizeStrategyAuto). | ||
| 313 | ResizeWidth(160). | ||
| 314 | Quality(75). | ||
| 315 | Output(w). | ||
| 316 | Apply() | ||
| 317 | } else { | ||
| 318 | io.Copy(w, res.Body) | ||
| 319 | } | ||
| 320 | } else { | ||
| 321 | if err := renderGopherDirectory(w, tpl, assetList, uri, hostport, res.Dir); err != nil { | ||
| 322 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 323 | Title: title, | ||
| 324 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 325 | Assets: assetList, | ||
| 326 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 327 | Error: true, | ||
| 328 | Protocol: "gopher", | ||
| 329 | }); e != nil { | ||
| 330 | log.Println("Template error: " + e.Error()) | ||
| 331 | log.Println(e.Error()) | ||
| 332 | } | ||
| 333 | } | ||
| 334 | } | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { | ||
| 339 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 340 | agent := req.UserAgent() | ||
| 341 | path := strings.TrimPrefix(req.URL.Path, "/gemini/") | ||
| 342 | |||
| 343 | if robotsdata != nil && robotsdebug && !robotsdata.TestAgent(path, agent) { | ||
| 344 | log.Printf("UserAgent %s ignored robots.txt", agent) | ||
| 345 | } | ||
| 346 | |||
| 347 | parts := strings.Split(path, "/") | ||
| 348 | hostport := parts[0] | ||
| 349 | |||
| 350 | if len(hostport) == 0 { | ||
| 351 | http.Redirect(w, req, "/", http.StatusFound) | ||
| 352 | return | ||
| 353 | } | ||
| 354 | |||
| 355 | title := hostport | ||
| 356 | |||
| 357 | var qs string | ||
| 358 | |||
| 359 | if req.URL.RawQuery != "" { | ||
| 360 | qs = fmt.Sprintf("?%s", url.QueryEscape(req.URL.RawQuery)) | ||
| 361 | } | ||
| 362 | |||
| 363 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) | ||
| 364 | if err != nil { | ||
| 365 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 366 | Title: title, | ||
| 367 | URI: hostport, | ||
| 368 | Assets: assetList, | ||
| 369 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 370 | Error: true, | ||
| 371 | Protocol: "gemini", | ||
| 372 | }); e != nil { | ||
| 373 | log.Println("Template error: " + e.Error()) | ||
| 374 | log.Println(err.Error()) | ||
| 375 | } | ||
| 376 | return | ||
| 377 | } | ||
| 378 | |||
| 379 | if uri != "" { | ||
| 380 | title = fmt.Sprintf("%s/%s", hostport, uri) | ||
| 381 | } | ||
| 382 | |||
| 383 | res, err := libgemini.Get( | ||
| 384 | fmt.Sprintf( | ||
| 385 | "gemini://%s/%s%s", | ||
| 386 | hostport, | ||
| 387 | uri, | ||
| 388 | qs, | ||
| 389 | ), | ||
| 390 | ) | ||
| 391 | |||
| 392 | if err != nil { | ||
| 393 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 394 | Title: title, | ||
| 395 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 396 | Assets: assetList, | ||
| 397 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 398 | Error: true, | ||
| 399 | Protocol: "gemini", | ||
| 400 | }); e != nil { | ||
| 401 | log.Println("Template error: " + e.Error()) | ||
| 402 | log.Println(err.Error()) | ||
| 403 | } | ||
| 404 | return | ||
| 405 | } | ||
| 406 | |||
| 407 | if int(res.Header.Status/10) == 3 { | ||
| 408 | baseURL, err := url.Parse(fmt.Sprintf( | ||
| 409 | "gemini://%s/%s", | ||
| 410 | hostport, | ||
| 411 | uri, | ||
| 412 | )) | ||
| 413 | if err != nil { | ||
| 414 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 415 | Title: title, | ||
| 416 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 417 | Assets: assetList, | ||
| 418 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 419 | Error: true, | ||
| 420 | Protocol: "gemini", | ||
| 421 | }); e != nil { | ||
| 422 | log.Println("Template error: " + e.Error()) | ||
| 423 | log.Println(err.Error()) | ||
| 424 | } | ||
| 425 | return | ||
| 426 | } | ||
| 427 | |||
| 428 | http.Redirect(w, req, resolveURI(res.Header.Meta, baseURL), http.StatusFound) | ||
| 429 | return | ||
| 430 | } | ||
| 431 | |||
| 432 | if int(res.Header.Status/10) != 2 { | ||
| 433 | if err := tpl.Execute(w, TemplateVariables{ | ||
| 434 | Title: title, | ||
| 435 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 436 | Assets: assetList, | ||
| 437 | RawText: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), | ||
| 438 | Error: true, | ||
| 439 | Protocol: "gemini", | ||
| 440 | }); err != nil { | ||
| 441 | log.Println("Template error: " + err.Error()) | ||
| 442 | } | ||
| 443 | return | ||
| 444 | } | ||
| 445 | |||
| 446 | if strings.HasPrefix(res.Header.Meta, "text/") { | ||
| 447 | buf := new(bytes.Buffer) | ||
| 448 | |||
| 449 | _, params, err := mime.ParseMediaType(res.Header.Meta) | ||
| 450 | if err != nil { | ||
| 451 | buf.ReadFrom(res.Body) | ||
| 452 | } else { | ||
| 453 | encoding, _ := charset.Lookup(params["charset"]) | ||
| 454 | readbuf := new(bytes.Buffer) | ||
| 455 | readbuf.ReadFrom(res.Body) | ||
| 456 | |||
| 457 | writer := transform.NewWriter(buf, encoding.NewDecoder()) | ||
| 458 | writer.Write(readbuf.Bytes()) | ||
| 459 | writer.Close() | ||
| 460 | } | ||
| 461 | |||
| 462 | var ( | ||
| 463 | rawText string | ||
| 464 | items []Item | ||
| 465 | ) | ||
| 466 | |||
| 467 | if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { | ||
| 468 | items = parseGeminiDocument(buf, uri, hostport) | ||
| 469 | } else { | ||
| 470 | rawText = buf.String() | ||
| 471 | } | ||
| 472 | |||
| 473 | if err := tpl.Execute(w, TemplateVariables{ | ||
| 474 | Title: title, | ||
| 475 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 476 | Assets: assetList, | ||
| 477 | Lines: items, | ||
| 478 | RawText: rawText, | ||
| 479 | Protocol: "gemini", | ||
| 480 | }); err != nil { | ||
| 481 | log.Println("Template error: " + err.Error()) | ||
| 482 | } | ||
| 483 | } else { | ||
| 484 | io.Copy(w, res.Body) | ||
| 485 | } | ||
| 486 | } | ||
| 487 | } | ||
| 488 | |||
| 489 | // RobotsTxtHandler returns the contents of the robots.txt file | ||
| 490 | // if configured and valid. | ||
| 491 | func RobotsTxtHandler(robotstxtdata []byte) http.HandlerFunc { | ||
| 492 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 493 | if robotstxtdata == nil { | ||
| 494 | http.Error(w, "Not Found", http.StatusNotFound) | ||
| 495 | return | ||
| 496 | } | ||
| 497 | |||
| 498 | w.Header().Set("Content-Type", "text/plain") | ||
| 499 | w.Write(robotstxtdata) | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | func FaviconHandler(favicondata []byte) http.HandlerFunc { | ||
| 504 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 505 | if favicondata == nil { | ||
| 506 | http.Error(w, "Not Found", http.StatusNotFound) | ||
| 507 | return | ||
| 508 | } | ||
| 509 | |||
| 510 | w.Header().Set("Content-Type", "image/vnd.microsoft.icon") | ||
| 511 | w.Header().Set("Cache-Control", "max-age=2592000") | ||
| 512 | w.Write(favicondata) | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 516 | func StyleHandler(styledata []byte) http.HandlerFunc { | ||
| 517 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 518 | w.Header().Set("Content-Type", "text/css") | ||
| 519 | w.Header().Set("Cache-Control", "max-age=2592000") | ||
| 520 | w.Write(styledata) | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | func JavaScriptHandler(jsdata []byte) http.HandlerFunc { | ||
| 525 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 526 | w.Header().Set("Content-Type", "text/javascript") | ||
| 527 | w.Header().Set("Cache-Control", "max-age=2592000") | ||
| 528 | w.Write(jsdata) | ||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | func FontHandler(woff2 bool, fontdata []byte) http.HandlerFunc { | ||
| 533 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 534 | if fontdata == nil { | ||
| 535 | http.Error(w, "Not Found", http.StatusNotFound) | ||
| 536 | return | ||
| 537 | } | ||
| 538 | |||
| 539 | if woff2 { | ||
| 540 | w.Header().Set("Content-Type", "font/woff2") | ||
| 541 | } else { | ||
| 542 | w.Header().Set("Content-Type", "font/woff") | ||
| 543 | } | ||
| 544 | w.Header().Set("Cache-Control", "max-age=2592000") | ||
| 545 | |||
| 546 | w.Write(fontdata) | ||
| 547 | } | ||
| 548 | } | ||
| 549 | |||
| 550 | // ListenAndServe creates a listening HTTP server bound to | ||
| 551 | // the interface specified by bind and sets up a Gopher to HTTP | ||
| 552 | // proxy proxying requests as requested and by default will prozy | ||
| 553 | // to a Gopher server address specified by uri if no servers is | ||
| 554 | // specified by the request. The robots argument is a pointer to | ||
| 555 | // a robotstxt.RobotsData struct for testing user agents against | ||
| 556 | // a configurable robots.txt file. | ||
| 557 | func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug bool, vipsconcurrency int) error { | ||
| 558 | var ( | ||
| 559 | tpl *template.Template | ||
| 560 | robotsdata *robotstxt.RobotsData | ||
| 561 | ) | ||
| 562 | |||
| 563 | robotstxtdata, err := ioutil.ReadFile(robotsfile) | ||
| 564 | if err != nil { | ||
| 565 | log.Printf("error reading robots.txt: %s", err) | ||
| 566 | robotstxtdata = nil | ||
| 567 | } else { | ||
| 568 | robotsdata, err = robotstxt.FromBytes(robotstxtdata) | ||
| 569 | if err != nil { | ||
| 570 | log.Printf("error reading robots.txt: %s", err) | ||
| 571 | robotstxtdata = nil | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | box := packr.New("assets", "../assets") | ||
| 576 | |||
| 577 | fontdataw, err := box.Find("iosevka-term-ss03-regular.woff") | ||
| 578 | if err != nil { | ||
| 579 | fontdataw = []byte{} | ||
| 580 | } | ||
| 581 | fontwAsset := fmt.Sprintf("/iosevka-term-ss03-regular-%x.woff", md5.Sum(fontdataw)) | ||
| 582 | |||
| 583 | fontdataw2, err := box.Find("iosevka-term-ss03-regular.woff2") | ||
| 584 | if err != nil { | ||
| 585 | fontdataw2 = []byte{} | ||
| 586 | } | ||
| 587 | fontw2Asset := fmt.Sprintf("/iosevka-term-ss03-regular-%x.woff2", md5.Sum(fontdataw2)) | ||
| 588 | |||
| 589 | propfontdataw, err := box.Find("iosevka-aile-regular.woff") | ||
| 590 | if err != nil { | ||
| 591 | propfontdataw = []byte{} | ||
| 592 | } | ||
| 593 | propfontwAsset := fmt.Sprintf("/iosevka-aile-regular-%x.woff", md5.Sum(propfontdataw)) | ||
| 594 | |||
| 595 | propfontdataw2, err := box.Find("iosevka-aile-regular.woff2") | ||
| 596 | if err != nil { | ||
| 597 | propfontdataw2 = []byte{} | ||
| 598 | } | ||
| 599 | propfontw2Asset := fmt.Sprintf("/iosevka-aile-regular-%x.woff2", md5.Sum(propfontdataw2)) | ||
| 600 | |||
| 601 | styledata, err := box.Find("style.css") | ||
| 602 | if err != nil { | ||
| 603 | styledata = []byte{} | ||
| 604 | } | ||
| 605 | styleAsset := fmt.Sprintf("/style-%x.css", md5.Sum(styledata)) | ||
| 606 | |||
| 607 | jsdata, err := box.Find("main.js") | ||
| 608 | if err != nil { | ||
| 609 | jsdata = []byte{} | ||
| 610 | } | ||
| 611 | jsAsset := fmt.Sprintf("/main-%x.js", md5.Sum(jsdata)) | ||
| 612 | |||
| 613 | favicondata, err := box.Find("favicon.ico") | ||
| 614 | if err != nil { | ||
| 615 | favicondata = []byte{} | ||
| 616 | } | ||
| 617 | |||
| 618 | startpagedata, err := ioutil.ReadFile(startpagefile) | ||
| 619 | if err != nil { | ||
| 620 | startpagedata, err = box.Find("startpage.txt") | ||
| 621 | if err != nil { | ||
| 622 | startpagedata = []byte{} | ||
| 623 | } | ||
| 624 | } | ||
| 625 | startpagetext := string(startpagedata) | ||
| 626 | |||
| 627 | tpldata, err := ioutil.ReadFile(".template") | ||
| 628 | if err == nil { | ||
| 629 | tpltext = string(tpldata) | ||
| 630 | } | ||
| 631 | |||
| 632 | funcMap := template.FuncMap{ | ||
| 633 | "safeHtml": func(s string) template.HTML { | ||
| 634 | return template.HTML(s) | ||
| 635 | }, | ||
| 636 | "safeCss": func(s string) template.CSS { | ||
| 637 | return template.CSS(s) | ||
| 638 | }, | ||
| 639 | "safeJs": func(s string) template.JS { | ||
| 640 | return template.JS(s) | ||
| 641 | }, | ||
| 642 | "HTMLEscape": func(s string) string { | ||
| 643 | return html.EscapeString(s) | ||
| 644 | }, | ||
| 645 | "split": strings.Split, | ||
| 646 | "last": func(s []string) string { | ||
| 647 | return s[len(s)-1] | ||
| 648 | }, | ||
| 649 | "pop": func(s []string) []string { | ||
| 650 | return s[:len(s)-1] | ||
| 651 | }, | ||
| 652 | "replace": func(pattern, output string, input interface{}) string { | ||
| 653 | var re = regexp.MustCompile(pattern) | ||
| 654 | var inputStr = fmt.Sprintf("%v", input) | ||
| 655 | return re.ReplaceAllString(inputStr, output) | ||
| 656 | }, | ||
| 657 | "trimLeftChar": func(s string) string { | ||
| 658 | for i := range s { | ||
| 659 | if i > 0 { | ||
| 660 | return s[i:] | ||
| 661 | } | ||
| 662 | } | ||
| 663 | return s[:0] | ||
| 664 | }, | ||
| 665 | "hasPrefix": func(s string, prefix string) bool { | ||
| 666 | return strings.HasPrefix(s, prefix) | ||
| 667 | }, | ||
| 668 | "title": func(s string) string { | ||
| 669 | return strings.Title(s) | ||
| 670 | }, | ||
| 671 | } | ||
| 672 | |||
| 673 | tpl, err = template.New("gophermenu").Funcs(funcMap).Parse(tpltext) | ||
| 674 | if err != nil { | ||
| 675 | log.Fatal(err) | ||
| 676 | } | ||
| 677 | |||
| 678 | vips.Startup(&vips.Config{ | ||
| 679 | ConcurrencyLevel: vipsconcurrency, | ||
| 680 | }) | ||
| 681 | |||
| 682 | assets := AssetList{ | ||
| 683 | Style: styleAsset, | ||
| 684 | JS: jsAsset, | ||
| 685 | FontW: fontwAsset, | ||
| 686 | FontW2: fontw2Asset, | ||
| 687 | PropFontW: propfontwAsset, | ||
| 688 | PropFontW2: propfontw2Asset, | ||
| 689 | } | ||
| 690 | |||
| 691 | http.Handle("/", gziphandler.GzipHandler(DefaultHandler(tpl, startpagetext, assets))) | ||
| 692 | http.Handle("/gopher/", gziphandler.GzipHandler(GopherHandler(tpl, robotsdata, assets, robotsdebug))) | ||
| 693 | http.Handle("/gemini/", gziphandler.GzipHandler(GeminiHandler(tpl, robotsdata, assets, robotsdebug))) | ||
| 694 | http.Handle("/robots.txt", gziphandler.GzipHandler(RobotsTxtHandler(robotstxtdata))) | ||
| 695 | http.Handle("/favicon.ico", gziphandler.GzipHandler(FaviconHandler(favicondata))) | ||
| 696 | http.Handle(styleAsset, gziphandler.GzipHandler(StyleHandler(styledata))) | ||
| 697 | http.Handle(jsAsset, gziphandler.GzipHandler(JavaScriptHandler(jsdata))) | ||
| 698 | http.HandleFunc(fontwAsset, FontHandler(false, fontdataw)) | ||
| 699 | http.HandleFunc(fontw2Asset, FontHandler(true, fontdataw2)) | ||
| 700 | http.HandleFunc(propfontwAsset, FontHandler(false, propfontdataw)) | ||
| 701 | http.HandleFunc(propfontw2Asset, FontHandler(true, propfontdataw2)) | ||
| 702 | //http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/")))) | ||
| 703 | |||
| 704 | return http.ListenAndServe(bind, nil) | ||
| 705 | } | ||
diff --git a/internal/port/gemini.go b/internal/port/gemini.go new file mode 100644 index 0000000..f9b0b97 --- /dev/null +++ b/internal/port/gemini.go | |||
| @@ -0,0 +1,205 @@ | |||
| 1 | package port | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bytes" | ||
| 5 | "fmt" | ||
| 6 | "html/template" | ||
| 7 | "io" | ||
| 8 | "log" | ||
| 9 | "mime" | ||
| 10 | "net/http" | ||
| 11 | "net/url" | ||
| 12 | "regexp" | ||
| 13 | "strings" | ||
| 14 | |||
| 15 | "golang.org/x/net/html/charset" | ||
| 16 | "golang.org/x/text/transform" | ||
| 17 | |||
| 18 | "git.vulpes.one/Feuerfuchs/port/port/libgemini" | ||
| 19 | |||
| 20 | "github.com/temoto/robotstxt" | ||
| 21 | ) | ||
| 22 | |||
| 23 | var ( | ||
| 24 | TermEscapeSGRPattern = regexp.MustCompile("\\[\\d+(;\\d+)*m") | ||
| 25 | ) | ||
| 26 | |||
| 27 | func resolveURI(uri string, baseURL *url.URL) (resolvedURI string) { | ||
| 28 | if strings.HasPrefix(uri, "//") { | ||
| 29 | resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "//") | ||
| 30 | } else if strings.HasPrefix(uri, "gemini://") { | ||
| 31 | resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "gemini://") | ||
| 32 | } else if strings.HasPrefix(uri, "gopher://") { | ||
| 33 | resolvedURI = "/gopher/" + strings.TrimPrefix(uri, "gopher://") | ||
| 34 | } else { | ||
| 35 | url, err := url.Parse(uri) | ||
| 36 | if err != nil { | ||
| 37 | return "" | ||
| 38 | } | ||
| 39 | adjustedURI := baseURL.ResolveReference(url) | ||
| 40 | path := adjustedURI.Path | ||
| 41 | if !strings.HasPrefix(path, "/") { | ||
| 42 | path = "/" + path | ||
| 43 | } | ||
| 44 | if adjustedURI.Scheme == "gemini" { | ||
| 45 | resolvedURI = "/gemini/" + adjustedURI.Host + path | ||
| 46 | } else if adjustedURI.Scheme == "gopher" { | ||
| 47 | resolvedURI = "/gopher/" + adjustedURI.Host + path | ||
| 48 | } else { | ||
| 49 | resolvedURI = adjustedURI.String() | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | return | ||
| 54 | } | ||
| 55 | |||
| 56 | func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { | ||
| 57 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 58 | agent := req.UserAgent() | ||
| 59 | path := strings.TrimPrefix(req.URL.Path, "/gemini/") | ||
| 60 | |||
| 61 | if robotsdata != nil && robotsdebug && !robotsdata.TestAgent(path, agent) { | ||
| 62 | log.Printf("UserAgent %s ignored robots.txt", agent) | ||
| 63 | } | ||
| 64 | |||
| 65 | parts := strings.Split(path, "/") | ||
| 66 | hostport := parts[0] | ||
| 67 | |||
| 68 | if len(hostport) == 0 { | ||
| 69 | http.Redirect(w, req, "/", http.StatusFound) | ||
| 70 | return | ||
| 71 | } | ||
| 72 | |||
| 73 | title := hostport | ||
| 74 | |||
| 75 | var qs string | ||
| 76 | |||
| 77 | if req.URL.RawQuery != "" { | ||
| 78 | qs = fmt.Sprintf("?%s", url.QueryEscape(req.URL.RawQuery)) | ||
| 79 | } | ||
| 80 | |||
| 81 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) | ||
| 82 | if err != nil { | ||
| 83 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 84 | Title: title, | ||
| 85 | URI: hostport, | ||
| 86 | Assets: assetList, | ||
| 87 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 88 | Error: true, | ||
| 89 | Protocol: "gemini", | ||
| 90 | }); e != nil { | ||
| 91 | log.Println("Template error: " + e.Error()) | ||
| 92 | log.Println(err.Error()) | ||
| 93 | } | ||
| 94 | return | ||
| 95 | } | ||
| 96 | |||
| 97 | if uri != "" { | ||
| 98 | title = fmt.Sprintf("%s/%s", hostport, uri) | ||
| 99 | } | ||
| 100 | |||
| 101 | res, err := libgemini.Get( | ||
| 102 | fmt.Sprintf( | ||
| 103 | "gemini://%s/%s%s", | ||
| 104 | hostport, | ||
| 105 | uri, | ||
| 106 | qs, | ||
| 107 | ), | ||
| 108 | ) | ||
| 109 | |||
| 110 | if err != nil { | ||
| 111 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 112 | Title: title, | ||
| 113 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 114 | Assets: assetList, | ||
| 115 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 116 | Error: true, | ||
| 117 | Protocol: "gemini", | ||
| 118 | }); e != nil { | ||
| 119 | log.Println("Template error: " + e.Error()) | ||
| 120 | log.Println(err.Error()) | ||
| 121 | } | ||
| 122 | return | ||
| 123 | } | ||
| 124 | |||
| 125 | if int(res.Header.Status/10) == 3 { | ||
| 126 | baseURL, err := url.Parse(fmt.Sprintf( | ||
| 127 | "gemini://%s/%s", | ||
| 128 | hostport, | ||
| 129 | uri, | ||
| 130 | )) | ||
| 131 | if err != nil { | ||
| 132 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 133 | Title: title, | ||
| 134 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 135 | Assets: assetList, | ||
| 136 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 137 | Error: true, | ||
| 138 | Protocol: "gemini", | ||
| 139 | }); e != nil { | ||
| 140 | log.Println("Template error: " + e.Error()) | ||
| 141 | log.Println(err.Error()) | ||
| 142 | } | ||
| 143 | return | ||
| 144 | } | ||
| 145 | |||
| 146 | http.Redirect(w, req, resolveURI(res.Header.Meta, baseURL), http.StatusFound) | ||
| 147 | return | ||
| 148 | } | ||
| 149 | |||
| 150 | if int(res.Header.Status/10) != 2 { | ||
| 151 | if err := tpl.Execute(w, TemplateVariables{ | ||
| 152 | Title: title, | ||
| 153 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 154 | Assets: assetList, | ||
| 155 | RawText: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), | ||
| 156 | Error: true, | ||
| 157 | Protocol: "gemini", | ||
| 158 | }); err != nil { | ||
| 159 | log.Println("Template error: " + err.Error()) | ||
| 160 | } | ||
| 161 | return | ||
| 162 | } | ||
| 163 | |||
| 164 | if strings.HasPrefix(res.Header.Meta, "text/") { | ||
| 165 | buf := new(bytes.Buffer) | ||
| 166 | |||
| 167 | _, params, err := mime.ParseMediaType(res.Header.Meta) | ||
| 168 | if err != nil { | ||
| 169 | buf.ReadFrom(res.Body) | ||
| 170 | } else { | ||
| 171 | encoding, _ := charset.Lookup(params["charset"]) | ||
| 172 | readbuf := new(bytes.Buffer) | ||
| 173 | readbuf.ReadFrom(res.Body) | ||
| 174 | |||
| 175 | writer := transform.NewWriter(buf, encoding.NewDecoder()) | ||
| 176 | writer.Write(readbuf.Bytes()) | ||
| 177 | writer.Close() | ||
| 178 | } | ||
| 179 | |||
| 180 | var ( | ||
| 181 | rawText string | ||
| 182 | items []Item | ||
| 183 | ) | ||
| 184 | |||
| 185 | if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { | ||
| 186 | items = parseGeminiDocument(buf, uri, hostport) | ||
| 187 | } else { | ||
| 188 | rawText = buf.String() | ||
| 189 | } | ||
| 190 | |||
| 191 | if err := tpl.Execute(w, TemplateVariables{ | ||
| 192 | Title: title, | ||
| 193 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 194 | Assets: assetList, | ||
| 195 | Lines: items, | ||
| 196 | RawText: rawText, | ||
| 197 | Protocol: "gemini", | ||
| 198 | }); err != nil { | ||
| 199 | log.Println("Template error: " + err.Error()) | ||
| 200 | } | ||
| 201 | } else { | ||
| 202 | io.Copy(w, res.Body) | ||
| 203 | } | ||
| 204 | } | ||
| 205 | } | ||
diff --git a/internal/port/gopher.go b/internal/port/gopher.go new file mode 100644 index 0000000..ebeb213 --- /dev/null +++ b/internal/port/gopher.go | |||
| @@ -0,0 +1,217 @@ | |||
| 1 | package port | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bytes" | ||
| 5 | "fmt" | ||
| 6 | "html/template" | ||
| 7 | "io" | ||
| 8 | "log" | ||
| 9 | "net" | ||
| 10 | "net/http" | ||
| 11 | "net/url" | ||
| 12 | "strings" | ||
| 13 | |||
| 14 | "git.vulpes.one/Feuerfuchs/port/port/libgopher" | ||
| 15 | |||
| 16 | "github.com/davidbyttow/govips/pkg/vips" | ||
| 17 | "github.com/temoto/robotstxt" | ||
| 18 | ) | ||
| 19 | |||
| 20 | type Item struct { | ||
| 21 | Link template.URL | ||
| 22 | Type string | ||
| 23 | Text string | ||
| 24 | } | ||
| 25 | |||
| 26 | func renderGopherDirectory(w http.ResponseWriter, tpl *template.Template, assetList AssetList, uri string, hostport string, d libgopher.Directory) error { | ||
| 27 | var title string | ||
| 28 | |||
| 29 | out := make([]Item, len(d.Items)) | ||
| 30 | |||
| 31 | for i, x := range d.Items { | ||
| 32 | if x.Type == libgopher.INFO && x.Selector == "TITLE" { | ||
| 33 | title = x.Description | ||
| 34 | continue | ||
| 35 | } | ||
| 36 | |||
| 37 | tr := Item{ | ||
| 38 | Text: x.Description, | ||
| 39 | Type: x.Type.String(), | ||
| 40 | } | ||
| 41 | |||
| 42 | if x.Type == libgopher.INFO { | ||
| 43 | out[i] = tr | ||
| 44 | continue | ||
| 45 | } | ||
| 46 | |||
| 47 | if strings.HasPrefix(x.Selector, "URL:") || strings.HasPrefix(x.Selector, "/URL:") { | ||
| 48 | link := strings.TrimPrefix(strings.TrimPrefix(x.Selector, "/"), "URL:") | ||
| 49 | if strings.HasPrefix(link, "gemini://") { | ||
| 50 | link = fmt.Sprintf( | ||
| 51 | "/gemini/%s", | ||
| 52 | strings.TrimPrefix(link, "gemini://"), | ||
| 53 | ) | ||
| 54 | } else if strings.HasPrefix(link, "gopher://") { | ||
| 55 | link = fmt.Sprintf( | ||
| 56 | "/gopher/%s", | ||
| 57 | strings.TrimPrefix(link, "gopher://"), | ||
| 58 | ) | ||
| 59 | } | ||
| 60 | tr.Link = template.URL(link) | ||
| 61 | } else { | ||
| 62 | var linkHostport string | ||
| 63 | if x.Port != "70" { | ||
| 64 | linkHostport = net.JoinHostPort(x.Host, x.Port) | ||
| 65 | } else { | ||
| 66 | linkHostport = x.Host | ||
| 67 | } | ||
| 68 | |||
| 69 | path := url.PathEscape(x.Selector) | ||
| 70 | path = strings.Replace(path, "%2F", "/", -1) | ||
| 71 | tr.Link = template.URL( | ||
| 72 | fmt.Sprintf( | ||
| 73 | "/gopher/%s/%s%s", | ||
| 74 | linkHostport, | ||
| 75 | string(byte(x.Type)), | ||
| 76 | path, | ||
| 77 | ), | ||
| 78 | ) | ||
| 79 | } | ||
| 80 | |||
| 81 | out[i] = tr | ||
| 82 | } | ||
| 83 | |||
| 84 | if title == "" { | ||
| 85 | if uri != "" { | ||
| 86 | title = fmt.Sprintf("%s/%s", hostport, uri) | ||
| 87 | } else { | ||
| 88 | title = hostport | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | return tpl.Execute(w, TemplateVariables{ | ||
| 93 | Title: title, | ||
| 94 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 95 | Assets: assetList, | ||
| 96 | Lines: out, | ||
| 97 | Protocol: "gopher", | ||
| 98 | }) | ||
| 99 | } | ||
| 100 | |||
| 101 | // GopherHandler returns a Handler that proxies requests | ||
| 102 | // to the specified Gopher server as denoated by the first argument | ||
| 103 | // to the request path and renders the content using the provided template. | ||
| 104 | // The optional robots parameters points to a robotstxt.RobotsData struct | ||
| 105 | // to test user agents against a configurable robotst.txt file. | ||
| 106 | func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { | ||
| 107 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 108 | agent := req.UserAgent() | ||
| 109 | path := strings.TrimPrefix(req.URL.Path, "/gopher/") | ||
| 110 | |||
| 111 | if robotsdata != nil && robotsdebug && !robotsdata.TestAgent(path, agent) { | ||
| 112 | log.Printf("UserAgent %s ignored robots.txt", agent) | ||
| 113 | } | ||
| 114 | |||
| 115 | parts := strings.Split(path, "/") | ||
| 116 | hostport := parts[0] | ||
| 117 | |||
| 118 | if len(hostport) == 0 { | ||
| 119 | http.Redirect(w, req, "/", http.StatusFound) | ||
| 120 | return | ||
| 121 | } | ||
| 122 | |||
| 123 | title := hostport | ||
| 124 | |||
| 125 | var qs string | ||
| 126 | |||
| 127 | if req.URL.RawQuery != "" { | ||
| 128 | qs = fmt.Sprintf("?%s", url.QueryEscape(req.URL.RawQuery)) | ||
| 129 | } | ||
| 130 | |||
| 131 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) | ||
| 132 | if err != nil { | ||
| 133 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 134 | Title: title, | ||
| 135 | URI: hostport, | ||
| 136 | Assets: assetList, | ||
| 137 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 138 | Error: true, | ||
| 139 | Protocol: "gopher", | ||
| 140 | }); e != nil { | ||
| 141 | log.Println("Template error: " + e.Error()) | ||
| 142 | log.Println(err.Error()) | ||
| 143 | } | ||
| 144 | return | ||
| 145 | } | ||
| 146 | |||
| 147 | if uri != "" { | ||
| 148 | title = fmt.Sprintf("%s/%s", hostport, uri) | ||
| 149 | } | ||
| 150 | |||
| 151 | res, err := libgopher.Get( | ||
| 152 | fmt.Sprintf( | ||
| 153 | "gopher://%s/%s%s", | ||
| 154 | hostport, | ||
| 155 | uri, | ||
| 156 | qs, | ||
| 157 | ), | ||
| 158 | ) | ||
| 159 | |||
| 160 | if err != nil { | ||
| 161 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 162 | Title: title, | ||
| 163 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 164 | Assets: assetList, | ||
| 165 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 166 | Error: true, | ||
| 167 | Protocol: "gopher", | ||
| 168 | }); e != nil { | ||
| 169 | log.Println("Template error: " + e.Error()) | ||
| 170 | } | ||
| 171 | return | ||
| 172 | } | ||
| 173 | |||
| 174 | if res.Body != nil { | ||
| 175 | if len(parts) < 2 { | ||
| 176 | io.Copy(w, res.Body) | ||
| 177 | } else if strings.HasPrefix(parts[1], "0") && !strings.HasSuffix(uri, ".xml") && !strings.HasSuffix(uri, ".asc") { | ||
| 178 | buf := new(bytes.Buffer) | ||
| 179 | buf.ReadFrom(res.Body) | ||
| 180 | |||
| 181 | if err := tpl.Execute(w, TemplateVariables{ | ||
| 182 | Title: title, | ||
| 183 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 184 | Assets: assetList, | ||
| 185 | RawText: buf.String(), | ||
| 186 | Protocol: "gopher", | ||
| 187 | }); err != nil { | ||
| 188 | log.Println("Template error: " + err.Error()) | ||
| 189 | } | ||
| 190 | } else if strings.HasPrefix(parts[1], "T") { | ||
| 191 | _, _, err = vips.NewTransform(). | ||
| 192 | Load(res.Body). | ||
| 193 | ResizeStrategy(vips.ResizeStrategyAuto). | ||
| 194 | ResizeWidth(160). | ||
| 195 | Quality(75). | ||
| 196 | Output(w). | ||
| 197 | Apply() | ||
| 198 | } else { | ||
| 199 | io.Copy(w, res.Body) | ||
| 200 | } | ||
| 201 | } else { | ||
| 202 | if err := renderGopherDirectory(w, tpl, assetList, uri, hostport, res.Dir); err != nil { | ||
| 203 | if e := tpl.Execute(w, TemplateVariables{ | ||
| 204 | Title: title, | ||
| 205 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
| 206 | Assets: assetList, | ||
| 207 | RawText: fmt.Sprintf("Error: %s", err), | ||
| 208 | Error: true, | ||
| 209 | Protocol: "gopher", | ||
| 210 | }); e != nil { | ||
| 211 | log.Println("Template error: " + e.Error()) | ||
| 212 | log.Println(e.Error()) | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | } | ||
| 217 | } | ||
diff --git a/internal/port/main.go b/internal/port/main.go new file mode 100644 index 0000000..5cdd794 --- /dev/null +++ b/internal/port/main.go | |||
| @@ -0,0 +1,301 @@ | |||
| 1 | package port | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "crypto/md5" | ||
| 5 | "fmt" | ||
| 6 | "html" | ||
| 7 | "html/template" | ||
| 8 | "io/ioutil" | ||
| 9 | "log" | ||
| 10 | "net/http" | ||
| 11 | "regexp" | ||
| 12 | "strings" | ||
| 13 | |||
| 14 | "github.com/NYTimes/gziphandler" | ||
| 15 | "github.com/davidbyttow/govips/pkg/vips" | ||
| 16 | "github.com/gobuffalo/packr/v2" | ||
| 17 | "github.com/temoto/robotstxt" | ||
| 18 | ) | ||
| 19 | |||
| 20 | type AssetList struct { | ||
| 21 | Style string | ||
| 22 | JS string | ||
| 23 | FontW string | ||
| 24 | FontW2 string | ||
| 25 | PropFontW string | ||
| 26 | PropFontW2 string | ||
| 27 | } | ||
| 28 | |||
| 29 | type TemplateVariables struct { | ||
| 30 | Title string | ||
| 31 | URI string | ||
| 32 | Assets AssetList | ||
| 33 | RawText string | ||
| 34 | Lines []Item | ||
| 35 | Error bool | ||
| 36 | Protocol string | ||
| 37 | } | ||
| 38 | |||
| 39 | func DefaultHandler(tpl *template.Template, startpagetext string, assetList AssetList) http.HandlerFunc { | ||
| 40 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 41 | if err := tpl.Execute(w, TemplateVariables{ | ||
| 42 | Title: "Gopher/Gemini proxy", | ||
| 43 | Assets: assetList, | ||
| 44 | RawText: startpagetext, | ||
| 45 | Protocol: "startpage", | ||
| 46 | }); err != nil { | ||
| 47 | log.Println("Template error: " + err.Error()) | ||
| 48 | } | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | // RobotsTxtHandler returns the contents of the robots.txt file | ||
| 53 | // if configured and valid. | ||
| 54 | func RobotsTxtHandler(robotstxtdata []byte) http.HandlerFunc { | ||
| 55 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 56 | if robotstxtdata == nil { | ||
| 57 | http.Error(w, "Not Found", http.StatusNotFound) | ||
| 58 | return | ||
| 59 | } | ||
| 60 | |||
| 61 | w.Header().Set("Content-Type", "text/plain") | ||
| 62 | w.Write(robotstxtdata) | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | func FaviconHandler(favicondata []byte) http.HandlerFunc { | ||
| 67 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 68 | if favicondata == nil { | ||
| 69 | http.Error(w, "Not Found", http.StatusNotFound) | ||
| 70 | return | ||
| 71 | } | ||
| 72 | |||
| 73 | w.Header().Set("Content-Type", "image/vnd.microsoft.icon") | ||
| 74 | w.Header().Set("Cache-Control", "max-age=2592000") | ||
| 75 | w.Write(favicondata) | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | func StyleHandler(styledata []byte) http.HandlerFunc { | ||
| 80 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 81 | w.Header().Set("Content-Type", "text/css") | ||
| 82 | w.Header().Set("Cache-Control", "max-age=2592000") | ||
| 83 | w.Write(styledata) | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | func JavaScriptHandler(jsdata []byte) http.HandlerFunc { | ||
| 88 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 89 | w.Header().Set("Content-Type", "text/javascript") | ||
| 90 | w.Header().Set("Cache-Control", "max-age=2592000") | ||
| 91 | w.Write(jsdata) | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | func FontHandler(woff2 bool, fontdata []byte) http.HandlerFunc { | ||
| 96 | return func(w http.ResponseWriter, req *http.Request) { | ||
| 97 | if fontdata == nil { | ||
| 98 | http.Error(w, "Not Found", http.StatusNotFound) | ||
| 99 | return | ||
| 100 | } | ||
| 101 | |||
| 102 | if woff2 { | ||
| 103 | w.Header().Set("Content-Type", "font/woff2") | ||
| 104 | } else { | ||
| 105 | w.Header().Set("Content-Type", "font/woff") | ||
| 106 | } | ||
| 107 | w.Header().Set("Cache-Control", "max-age=2592000") | ||
| 108 | |||
| 109 | w.Write(fontdata) | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | // ListenAndServe creates a listening HTTP server bound to | ||
| 114 | // the interface specified by bind and sets up a Gopher to HTTP | ||
| 115 | // proxy proxying requests as requested and by default will prozy | ||
| 116 | // to a Gopher server address specified by uri if no servers is | ||
| 117 | // specified by the request. The robots argument is a pointer to | ||
| 118 | // a robotstxt.RobotsData struct for testing user agents against | ||
| 119 | // a configurable robots.txt file. | ||
| 120 | func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug bool, vipsconcurrency int) error { | ||
| 121 | box := packr.New("assets", "../assets") | ||
| 122 | |||
| 123 | // | ||
| 124 | // Robots | ||
| 125 | |||
| 126 | var robotsdata *robotstxt.RobotsData | ||
| 127 | |||
| 128 | robotstxtdata, err := ioutil.ReadFile(robotsfile) | ||
| 129 | if err != nil { | ||
| 130 | log.Printf("error reading robots.txt: %s", err) | ||
| 131 | robotstxtdata = nil | ||
| 132 | } else { | ||
| 133 | robotsdata, err = robotstxt.FromBytes(robotstxtdata) | ||
| 134 | if err != nil { | ||
| 135 | log.Printf("error reading robots.txt: %s", err) | ||
| 136 | robotstxtdata = nil | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | // | ||
| 141 | // Fonts | ||
| 142 | |||
| 143 | fontdataw, err := box.Find("iosevka-term-ss03-regular.woff") | ||
| 144 | if err != nil { | ||
| 145 | fontdataw = []byte{} | ||
| 146 | } | ||
| 147 | fontwAsset := fmt.Sprintf("/iosevka-term-ss03-regular-%x.woff", md5.Sum(fontdataw)) | ||
| 148 | |||
| 149 | fontdataw2, err := box.Find("iosevka-term-ss03-regular.woff2") | ||
| 150 | if err != nil { | ||
| 151 | fontdataw2 = []byte{} | ||
| 152 | } | ||
| 153 | fontw2Asset := fmt.Sprintf("/iosevka-term-ss03-regular-%x.woff2", md5.Sum(fontdataw2)) | ||
| 154 | |||
| 155 | propfontdataw, err := box.Find("iosevka-aile-regular.woff") | ||
| 156 | if err != nil { | ||
| 157 | propfontdataw = []byte{} | ||
| 158 | } | ||
| 159 | propfontwAsset := fmt.Sprintf("/iosevka-aile-regular-%x.woff", md5.Sum(propfontdataw)) | ||
| 160 | |||
| 161 | propfontdataw2, err := box.Find("iosevka-aile-regular.woff2") | ||
| 162 | if err != nil { | ||
| 163 | propfontdataw2 = []byte{} | ||
| 164 | } | ||
| 165 | propfontw2Asset := fmt.Sprintf("/iosevka-aile-regular-%x.woff2", md5.Sum(propfontdataw2)) | ||
| 166 | |||
| 167 | // | ||
| 168 | // Stylesheet | ||
| 169 | |||
| 170 | styledata, err := box.Find("style.css") | ||
| 171 | if err != nil { | ||
| 172 | styledata = []byte{} | ||
| 173 | } | ||
| 174 | styleAsset := fmt.Sprintf("/style-%x.css", md5.Sum(styledata)) | ||
| 175 | |||
| 176 | // | ||
| 177 | // JavaScript | ||
| 178 | |||
| 179 | jsdata, err := box.Find("main.js") | ||
| 180 | if err != nil { | ||
| 181 | jsdata = []byte{} | ||
| 182 | } | ||
| 183 | jsAsset := fmt.Sprintf("/main-%x.js", md5.Sum(jsdata)) | ||
| 184 | |||
| 185 | // | ||
| 186 | // Favicon | ||
| 187 | |||
| 188 | favicondata, err := box.Find("favicon.ico") | ||
| 189 | if err != nil { | ||
| 190 | favicondata = []byte{} | ||
| 191 | } | ||
| 192 | |||
| 193 | // | ||
| 194 | // Start page text | ||
| 195 | |||
| 196 | startpagedata, err := ioutil.ReadFile(startpagefile) | ||
| 197 | if err != nil { | ||
| 198 | startpagedata, err = box.Find("startpage.txt") | ||
| 199 | if err != nil { | ||
| 200 | startpagedata = []byte{} | ||
| 201 | } | ||
| 202 | } | ||
| 203 | startpagetext := string(startpagedata) | ||
| 204 | |||
| 205 | // | ||
| 206 | // | ||
| 207 | |||
| 208 | var allFiles []string | ||
| 209 | files, err := ioutil.ReadDir("./tpl") | ||
| 210 | if err != nil { | ||
| 211 | fmt.Println(err) | ||
| 212 | } | ||
| 213 | for _, file := range files { | ||
| 214 | filename := file.Name() | ||
| 215 | if strings.HasSuffix(filename, ".html") { | ||
| 216 | allFiles = append(allFiles, "./tpl/"+filename) | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | templates, err = template.ParseFiles(allFiles...) | ||
| 221 | |||
| 222 | // | ||
| 223 | |||
| 224 | funcMap := template.FuncMap{ | ||
| 225 | "safeHtml": func(s string) template.HTML { | ||
| 226 | return template.HTML(s) | ||
| 227 | }, | ||
| 228 | "safeCss": func(s string) template.CSS { | ||
| 229 | return template.CSS(s) | ||
| 230 | }, | ||
| 231 | "safeJs": func(s string) template.JS { | ||
| 232 | return template.JS(s) | ||
| 233 | }, | ||
| 234 | "HTMLEscape": func(s string) string { | ||
| 235 | return html.EscapeString(s) | ||
| 236 | }, | ||
| 237 | "split": strings.Split, | ||
| 238 | "last": func(s []string) string { | ||
| 239 | return s[len(s)-1] | ||
| 240 | }, | ||
| 241 | "pop": func(s []string) []string { | ||
| 242 | return s[:len(s)-1] | ||
| 243 | }, | ||
| 244 | "replace": func(pattern, output string, input interface{}) string { | ||
| 245 | var re = regexp.MustCompile(pattern) | ||
| 246 | var inputStr = fmt.Sprintf("%v", input) | ||
| 247 | return re.ReplaceAllString(inputStr, output) | ||
| 248 | }, | ||
| 249 | "trimLeftChar": func(s string) string { | ||
| 250 | for i := range s { | ||
| 251 | if i > 0 { | ||
| 252 | return s[i:] | ||
| 253 | } | ||
| 254 | } | ||
| 255 | return s[:0] | ||
| 256 | }, | ||
| 257 | "hasPrefix": func(s string, prefix string) bool { | ||
| 258 | return strings.HasPrefix(s, prefix) | ||
| 259 | }, | ||
| 260 | "title": func(s string) string { | ||
| 261 | return strings.Title(s) | ||
| 262 | }, | ||
| 263 | } | ||
| 264 | |||
| 265 | // | ||
| 266 | |||
| 267 | startpageTpl := templates.Lookup("startpage.html").Funcs(funcMap) | ||
| 268 | geminiTpl := templates.Lookup("gemini.html").Funcs(funcMap) | ||
| 269 | gopherTpl := templates.Lookup("gopher.html").Funcs(funcMap) | ||
| 270 | |||
| 271 | // | ||
| 272 | // | ||
| 273 | |||
| 274 | vips.Startup(&vips.Config{ | ||
| 275 | ConcurrencyLevel: vipsconcurrency, | ||
| 276 | }) | ||
| 277 | |||
| 278 | assets := AssetList{ | ||
| 279 | Style: styleAsset, | ||
| 280 | JS: jsAsset, | ||
| 281 | FontW: fontwAsset, | ||
| 282 | FontW2: fontw2Asset, | ||
| 283 | PropFontW: propfontwAsset, | ||
| 284 | PropFontW2: propfontw2Asset, | ||
| 285 | } | ||
| 286 | |||
| 287 | http.Handle("/", gziphandler.GzipHandler(DefaultHandler(startpageTpl, startpagetext, assets))) | ||
| 288 | http.Handle("/gopher/", gziphandler.GzipHandler(GopherHandler(gopherTpl, robotsdata, assets, robotsdebug))) | ||
| 289 | http.Handle("/gemini/", gziphandler.GzipHandler(GeminiHandler(geminiTpl, robotsdata, assets, robotsdebug))) | ||
| 290 | http.Handle("/robots.txt", gziphandler.GzipHandler(RobotsTxtHandler(robotstxtdata))) | ||
| 291 | http.Handle("/favicon.ico", gziphandler.GzipHandler(FaviconHandler(favicondata))) | ||
| 292 | http.Handle(styleAsset, gziphandler.GzipHandler(StyleHandler(styledata))) | ||
| 293 | http.Handle(jsAsset, gziphandler.GzipHandler(JavaScriptHandler(jsdata))) | ||
| 294 | http.HandleFunc(fontwAsset, FontHandler(false, fontdataw)) | ||
| 295 | http.HandleFunc(fontw2Asset, FontHandler(true, fontdataw2)) | ||
| 296 | http.HandleFunc(propfontwAsset, FontHandler(false, propfontdataw)) | ||
| 297 | http.HandleFunc(propfontw2Asset, FontHandler(true, propfontdataw2)) | ||
| 298 | //http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/")))) | ||
| 299 | |||
| 300 | return http.ListenAndServe(bind, nil) | ||
| 301 | } | ||
diff --git a/internal/port/tpl/gemini.html b/internal/port/tpl/gemini.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/internal/port/tpl/gemini.html | |||
diff --git a/internal/port/tpl/gopher.html b/internal/port/tpl/gopher.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/internal/port/tpl/gopher.html | |||
diff --git a/gopherproxy/template.go b/internal/port/tpl/startpage.html index 6b90cc0..8482a6f 100644 --- a/gopherproxy/template.go +++ b/internal/port/tpl/startpage.html | |||
| @@ -1,6 +1,4 @@ | |||
| 1 | package gopherproxy | 1 | <!doctype html> |
| 2 | |||
| 3 | var tpltext = `<!doctype html> | ||
| 4 | <html> | 2 | <html> |
| 5 | <head> | 3 | <head> |
| 6 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| @@ -119,4 +117,4 @@ var tpltext = `<!doctype html> | |||
| 119 | </aside> | 117 | </aside> |
| 120 | <script src="{{ .Assets.JS }}"></script> | 118 | <script src="{{ .Assets.JS }}"></script> |
| 121 | </body> | 119 | </body> |
| 122 | </html>` | 120 | </html> |
diff --git a/gopherproxy/libgemini/libgemini.go b/pkg/libgemini/libgemini.go index 303490c..303490c 100644 --- a/gopherproxy/libgemini/libgemini.go +++ b/pkg/libgemini/libgemini.go | |||
diff --git a/gopherproxy/libgopher/libgopher.go b/pkg/libgopher/libgopher.go index 86d58ff..86d58ff 100644 --- a/gopherproxy/libgopher/libgopher.go +++ b/pkg/libgopher/libgopher.go | |||
