Commit 645849c2 authored by Simon Kirsten's avatar Simon Kirsten
Browse files

Merge branch 'tls-support-namespaces-monitoring' into 'master'

Added TLS support, namespaces and monitoring

See merge request !3
parents 4470a41a 8aac8104
Pipeline #43965 passed with stages
in 4 minutes and 10 seconds
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
server: server:
stage: build stage: build
image: golang:1.13 image: golang:1.14
before_script: before_script:
- go mod download - go mod download
......
# Changelog # Changelog
### v1.0.0 <small>_June 20, 2020_</small>
* Server: Added TLS support
* Server: Added namespace support
* Server: Added monitoring (prometheus metric exporter)
* Display: Improved player
- Made players clickable to unmute and acknowledge mature audiences warning.
- Fixed chat sometimes showing when it is not supposed to be
- Minor fixes
### v0.9.15 <small>_June 16, 2020_</small> ### v0.9.15 <small>_June 16, 2020_</small>
* Server: Fixed chat * Server: Fixed chat
* Server: Fixed `volume=0` not working * Server: Fixed `volume=0` not working
...@@ -102,4 +111,4 @@ ...@@ -102,4 +111,4 @@
### v0.9.0 <small>_August 21, 2019_</small> ### v0.9.0 <small>_August 21, 2019_</small>
* Initial Release * Initial Release
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
6MF9+Yw1Yy0t
-----END CERTIFICATE-----
...@@ -15,11 +15,15 @@ import ( ...@@ -15,11 +15,15 @@ import (
const docURL = "https://stream-server.h-da.io" const docURL = "https://stream-server.h-da.io"
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
var port int var port int
var flagBrowser bool var tlsport int
var flagLocal bool var tlsCert, tlsKey string
var listenAddr string
var flagUseHostNamespace bool
var flagHelp bool var flagHelp bool
app.Usage = docURL app.Usage = docURL
...@@ -29,20 +33,37 @@ func main() { ...@@ -29,20 +33,37 @@ func main() {
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
cli.IntFlag{ cli.IntFlag{
Name: "port, p", Name: "port",
Value: 8080, Value: 8080,
Usage: "http port to listen on", Usage: "http port to listen on",
Destination: &port, Destination: &port,
}, },
cli.BoolFlag{ cli.IntFlag{
Name: "browser, b", Name: "tlsport",
Usage: "automatically open the default browser", Value: 8443,
Destination: &flagBrowser, Usage: "https port to listen on",
Destination: &tlsport,
},
cli.StringFlag{
Name: "listen",
Value: "0.0.0.0",
Usage: "address to listen on",
Destination: &listenAddr,
},
cli.StringFlag{
Name: "tlscert",
Usage: "TLS cert",
Destination: &tlsCert,
},
cli.StringFlag{
Name: "tlskey",
Usage: "TLS key",
Destination: &tlsKey,
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "local, l", Name: "use-host-namespace",
Usage: "only listen on 127.0.0.1 (see doc)", Usage: "use the Host header as namespace",
Destination: &flagLocal, Destination: &flagUseHostNamespace,
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "help, h", Name: "help, h",
...@@ -61,36 +82,56 @@ func main() { ...@@ -61,36 +82,56 @@ func main() {
return fmt.Errorf("Unknown arguments: %v", c.Args()) return fmt.Errorf("Unknown arguments: %v", c.Args())
} }
localhost := fmt.Sprintf("127.0.0.1:%d", port) withTLS := tlsCert != "" && tlsKey != ""
all := fmt.Sprintf("0.0.0.0:%d", port)
outbound := localhost
if outboundIP, err := util.GetOutboundIP(); err == nil {
outbound = fmt.Sprintf("%s:%d", outboundIP, port)
}
var listenAddr string fmt.Print("Serving on\n")
if flagLocal { if listenAddr == "0.0.0.0" {
listenAddr = localhost fmt.Printf(" http://%s:%d\n", "localhost", port)
if withTLS {
fmt.Printf(" https://%s:%d\n", "localhost", tlsport)
}
fmt.Printf("Serving on http://%s\n", localhost) if outboundIP, err := util.GetOutboundIP(); err == nil {
fmt.Printf(" http://%s:%d\n", outboundIP, port)
if withTLS {
fmt.Printf(" https://%s:%d\n", outboundIP, tlsport)
}
}
} else { } else {
listenAddr = all fmt.Printf(" http://%s:%d\n", listenAddr, port)
if withTLS {
fmt.Printf("Serving on\n http://%s\n http://%s\n", localhost, outbound) fmt.Printf(" https://%s:%d\n", listenAddr, tlsport)
}
if flagBrowser {
err := util.OpenBrowser(fmt.Sprintf("http://%s", localhost))
if err != nil {
fmt.Printf("Could not automatically open browser: %v\n", err)
} }
} }
fmt.Printf("Read the quickstart at %s/quickstart to get started.\n", docURL) fmt.Printf("Read the quickstart at %s/quickstart to get started.\n", docURL)
fmt.Printf("Stop with Ctrl-C or close this terminal.\n") fmt.Printf("Stop with Ctrl-C or close this terminal.\n")
return server.ListenAndServe(listenAddr) router := server.GetRouter(flagUseHostNamespace)
for {
errs := make(chan error)
go func() {
if err := server.ServeHTTP(fmt.Sprintf("%s:%d", listenAddr, port), router); err != nil {
errs <- err
}
}()
if withTLS {
go func() {
if err := server.ServeHTTPS(fmt.Sprintf("%s:%d", listenAddr, tlsport), router, tlsCert, tlsKey); err != nil {
errs <- err
}
}()
}
select {
case err := <-errs:
return err
}
}
} }
err := app.Run(os.Args) err := app.Run(os.Args)
......
...@@ -14,4 +14,4 @@ This is a very simple helper to call the Stream Server. See the [reference](refe ...@@ -14,4 +14,4 @@ This is a very simple helper to call the Stream Server. See the [reference](refe
<!-- Insert StreamServerClient.java --> <!-- Insert StreamServerClient.java -->
``` java linenums="1" ``` java linenums="1"
--8<-- "docs/assets/StreamServerClient.java" --8<-- "docs/assets/StreamServerClient.java"
``` ```
\ No newline at end of file
...@@ -59,6 +59,5 @@ Darwin (macOS) | 64 bit | [stream-server](/bin/~~~${VERSION}~~~/darwin-x86_64/s ...@@ -59,6 +59,5 @@ Darwin (macOS) | 64 bit | [stream-server](/bin/~~~${VERSION}~~~/darwin-x86_64/s
* listen tcp 0.0.0.0:8080: bind: address already in use * listen tcp 0.0.0.0:8080: bind: address already in use
> Something is already using the port. This could be another Stream Server instance or some other application (like a webserver). > Something is already using the port. This could be another Stream Server instance or some other application (like a webserver).
> Close / disable the other service or change the port via the [command-line options](options.md). > Close / disable the other service or change the port via the [command-line options](options.md).
...@@ -17,24 +17,35 @@ USAGE: ...@@ -17,24 +17,35 @@ USAGE:
stream-server [global options] [arguments...] stream-server [global options] [arguments...]
VERSION: VERSION:
v0.9.9 v1.0.0
GLOBAL OPTIONS: GLOBAL OPTIONS:
--port value, -p value http port to listen on (default: 8080) --port value http port to listen on (default: 8080)
--browser, -b automatically open the default browser --tlsport value https port to listen on (default: 8443)
--local, -l only listen on 127.0.0.1 (see doc) --listen value address to listen on (default: "0.0.0.0")
--help, -h show help --tlscert value TLS cert
--version, -v print the version --tlskey value TLS key
--use-host-namespace use the Host header as namespace
--help, -h show help
--version, -v print the version
``` ```
By default stream-server listens on all ip addresses on port 8080. This means that it is exposed to the local network (LAN / WLAN) which is needed if the app is on a phone. Note that the phone must be in the same network as the computer / laptop. By default stream-server listens on all ip addresses on port 8080. This means that it is exposed to the local network (LAN / WLAN) which is needed if the app is on a phone. Note that the phone must be in the same network as the computer / laptop.
When using the Android Emulator on the same device as the server this exposure is not necessary. Use the `--local` flag to only listen on `127.0.0.1` aka `localhost`. The emulator must then connect to `10.0.2.2` as described here: When using the Android Emulator on the same device as the server this exposure is not necessary. Use the `--listen 127.0.0.1` flag to only listen on `127.0.0.1` aka `localhost`. The emulator must then connect to `10.0.2.2` as described here:
> Also note that the address 127.0.0.1 on your development machine corresponds to the emulator's own loopback interface. If you want to access services running on your development machine loopback interface (a.k.a. 127.0.0.1 on your machine), you should use the special address 10.0.2.2 instead. > Also note that the address 127.0.0.1 on your development machine corresponds to the emulator's own loopback interface. If you want to access services running on your development machine loopback interface (a.k.a. 127.0.0.1 on your machine), you should use the special address 10.0.2.2 instead.
> >
> [Android Studio - User guide](https://developer.android.com/studio/run/emulator-networking#networkaddresses) > [Android Studio - User guide](https://developer.android.com/studio/run/emulator-networking#networkaddresses)
## Advanced configuration
- HTTPS can be enable by providing a TLS cert and key. If both config values are present a additional HTTPS server is spawned on the tlsport.
- By default the stream-server has only one namespace, meaning that all incoming requests reference the same display.
With `--use-host-namespace` the _Host_ header is used as a namespace. This means that each different host has a different display.
This can be used to dynamically host different displays on one stream-server instance.
New displays are created on demand.
## Environment Variables ## Environment Variables
The `TWITCH_CLIENT_ID` environment variable can be set. This is only necessary during development to access the Twitch API. All official release builds ship with a default client id but you can still supply your own if you want. The `TWITCH_CLIENT_ID` environment variable can be set. This is only necessary during development to access the Twitch API. All official release builds ship with a default client id but you can still supply your own if you want.
...@@ -5,8 +5,8 @@ $ ./stream-server ...@@ -5,8 +5,8 @@ $ ./stream-server
``` ```
``` ```
Serving on Serving on
http://127.0.0.1:8080 http://localhost:8080
http://192.168.0.66:8080 http://10.0.2.15:8080
Read the quickstart at https://stream-server.h-da.io/quickstart to get started. Read the quickstart at https://stream-server.h-da.io/quickstart to get started.
Stop with Ctrl-C or close this terminal. Stop with Ctrl-C or close this terminal.
``` ```
...@@ -15,10 +15,10 @@ Stop with Ctrl-C or close this terminal. ...@@ -15,10 +15,10 @@ Stop with Ctrl-C or close this terminal.
In the examples of this documentation only the local address is used. If you want to connect from your phone or the emulator you need to use the network address (`192.168.0.66` in this case). Also we assume you use the default port `8080`. In the examples of this documentation only the local address is used. If you want to connect from your phone or the emulator you need to use the network address (`192.168.0.66` in this case). Also we assume you use the default port `8080`.
1. Open <http://127.0.0.1:8080> in a browser. Leave this tab / window open while you do the other steps. 1. Open <http://localhost:8080> in a browser. Leave this tab / window open while you do the other steps.
2. Get the current state of the display: 2. Get the current state of the display:
<pre><code><http://127.0.0.1:8080/display></code></pre> <pre><code><http://localhost:8080/display></code></pre>
``` java ``` java
StreamServerClient client = new StreamServerClient("10.0.2.2:8080"); StreamServerClient client = new StreamServerClient("10.0.2.2:8080");
...@@ -38,7 +38,7 @@ Stop with Ctrl-C or close this terminal. ...@@ -38,7 +38,7 @@ Stop with Ctrl-C or close this terminal.
3. Find some stream on twitch: 3. Find some stream on twitch:
<pre><code><http://127.0.0.1:8080/twitch/getTopStreams></code></pre> <pre><code><http://localhost:8080/twitch/getTopStreams></code></pre>
``` java ``` java
List<JSONObject> topStreams = client.twitchGetTopStreams(null, null, null); // no filters List<JSONObject> topStreams = client.twitchGetTopStreams(null, null, null); // no filters
``` ```
...@@ -66,9 +66,9 @@ Stop with Ctrl-C or close this terminal. ...@@ -66,9 +66,9 @@ Stop with Ctrl-C or close this terminal.
4. Start playback of your selected stream with chat and muted volume: 4. Start playback of your selected stream with chat and muted volume:
<pre><code><http://127.0.0.1:8080/display?large_channel={channel name here}&volume=0&show_chat=true></code></pre> <pre><code><http://localhost:8080/display?large_channel=CHANNEL-NAME&volume=0&show_chat=true></code></pre>
``` java ``` java
client.display("channel name here", null, 0.0f, null, true); client.display("CHANNEL-NAME", null, 0.0f, null, true);
``` ```
!!! example !!! example
...@@ -84,4 +84,4 @@ Stop with Ctrl-C or close this terminal. ...@@ -84,4 +84,4 @@ Stop with Ctrl-C or close this terminal.
The tab / window from step 1 should now start playing that stream with the provided settings. The tab / window from step 1 should now start playing that stream with the provided settings.
5. Read the [Reference](reference.md). 5. Read the [Reference](reference.md).
\ No newline at end of file
...@@ -21,14 +21,14 @@ Returns [`DisplayState`](#displaystate). ...@@ -21,14 +21,14 @@ Returns [`DisplayState`](#displaystate).
??? example "Examples" ??? example "Examples"
* Only set the large_channel to riotgames * Only set the large_channel to riotgames
<pre><code><http://127.0.0.1:8080/display?large_channel=riotgames></code></pre> <pre><code><http://localhost:8080/display?large_channel=riotgames></code></pre>
``` java ``` java
/* JSONObject newState = */ client.display("riotgames", null, null, null, null); /* JSONObject newState = */ client.display("riotgames", null, null, null, null);
``` ```
* Set the large_channel to monstercat, disable the small_channel, set the volume to 0.8 and hide the chat. * Set the large_channel to monstercat, disable the small_channel, set the volume to 0.8 and hide the chat.
Notice the small_scale is not changed (because of its absence) Notice the small_scale is not changed (because of its absence)
<pre><code><http://127.0.0.1:8080/display?large_channel=monstercat&small_channel=&volume=0.8&show_chat=false></code></pre> <pre><code><http://localhost:8080/display?large_channel=monstercat&small_channel=&volume=0.8&show_chat=false></code></pre>
``` java ``` java
/* JSONObject newState = */ client.display("monstercat", "", 0.8f, null, false); /* JSONObject newState = */ client.display("monstercat", "", 0.8f, null, false);
``` ```
...@@ -47,7 +47,7 @@ Returns [`Game`](#game) array. ...@@ -47,7 +47,7 @@ Returns [`Game`](#game) array.
??? example "Examples" ??? example "Examples"
* Get top games * Get top games
<pre><code><http://127.0.0.1:8080/twitch/getTopGames></code></pre> <pre><code><http://localhost:8080/twitch/getTopGames></code></pre>
``` java ``` java
List<JSONObject> topGames = client.twitchGetTopGames(); List<JSONObject> topGames = client.twitchGetTopGames();
``` ```
...@@ -67,13 +67,13 @@ Returns [`Game`](#game) array. ...@@ -67,13 +67,13 @@ Returns [`Game`](#game) array.
??? example "Examples" ??? example "Examples"
* Search for the "game" Talk Shows & Podcasts * Search for the "game" Talk Shows & Podcasts
<pre><code><http://127.0.0.1:8080/twitch/searchGames?query=talk%20show></code></pre> <pre><code><http://localhost:8080/twitch/searchGames?query=talk%20show></code></pre>
``` java ``` java
List<JSONObject> foundGames = client.twitchSearchGames("talk show"); List<JSONObject> foundGames = client.twitchSearchGames("talk show");
``` ```
* Search for ove (will show games starting with "ove" like Overwatch) * Search for ove (will show games starting with "ove" like Overwatch)
<pre><code><http://127.0.0.1:8080/twitch/searchGames?query=ove></code></pre> <pre><code><http://localhost:8080/twitch/searchGames?query=ove></code></pre>
``` java ``` java
List<JSONObject> foundGames = client.twitchSearchGames("ove"); List<JSONObject> foundGames = client.twitchSearchGames("ove");
``` ```
...@@ -99,25 +99,25 @@ Returns [`Stream`](#stream) array. ...@@ -99,25 +99,25 @@ Returns [`Stream`](#stream) array.
??? example "Examples" ??? example "Examples"
* Get the unfiltered top streams * Get the unfiltered top streams
<pre><code><http://127.0.0.1:8080/twitch/getTopStreams></code></pre> <pre><code><http://localhost:8080/twitch/getTopStreams></code></pre>
``` java ``` java
List<JSONObject> topStreams = client.twitchGetTopStreams(null, null, null); List<JSONObject> topStreams = client.twitchGetTopStreams(null, null, null);
``` ```
* German top streams * German top streams
<pre><code><http://127.0.0.1:8080/twitch/getTopStreams?language=de></code></pre> <pre><code><http://localhost:8080/twitch/getTopStreams?language=de></code></pre>
``` java ``` java
List<JSONObject> topStreams = client.twitchGetTopStreams(null, null, "de"); List<JSONObject> topStreams = client.twitchGetTopStreams(null, null, "de");
``` ```
* English top streams in Talk Shows and Podcasts * English top streams in Talk Shows and Podcasts
<pre><code><http://127.0.0.1:8080/twitch/getTopStreams?language=en&game=Talk%20Shows%20%26%20Podcasts></code></pre> <pre><code><http://localhost:8080/twitch/getTopStreams?language=en&game=Talk%20Shows%20%26%20Podcasts></code></pre>
``` java ``` java
List<JSONObject> topStreams = client.twitchGetTopStreams(null, "Talk Shows and Podcasts", "en"); List<JSONObject> topStreams = client.twitchGetTopStreams(null, "Talk Shows and Podcasts", "en");
``` ```
* Tip: If your app implements a favorite list you can very easily query these channels and see who is streaming and other information * Tip: If your app implements a favorite list you can very easily query these channels and see who is streaming and other information
<pre><code><http://127.0.0.1:8080/twitch/getTopStreams?channels=xqcow,dafran,kitboga></code></pre> <pre><code><http://localhost:8080/twitch/getTopStreams?channels=xqcow,dafran,kitboga></code></pre>
``` java ``` java
String[] favoriteChannels = {"xqcow", "dafran", "kitboga"}; String[] favoriteChannels = {"xqcow", "dafran", "kitboga"};
...@@ -136,7 +136,7 @@ Returns [`Stream`](#stream) array. ...@@ -136,7 +136,7 @@ Returns [`Stream`](#stream) array.
??? example "Examples" ??? example "Examples"
* Get featured streams * Get featured streams
<pre><code><http://127.0.0.1:8080/twitch/getFeaturedStreams></code></pre> <pre><code><http://localhost:8080/twitch/getFeaturedStreams></code></pre>
``` java ``` java
List<JSONObject> featuredStreams = client.twitchGetFeaturedStreams(); List<JSONObject> featuredStreams = client.twitchGetFeaturedStreams();
``` ```
...@@ -188,4 +188,4 @@ Returns [`Stream`](#stream) array. ...@@ -188,4 +188,4 @@ Returns [`Stream`](#stream) array.
* The image at the `preview_img_url` url updates itself every couple of seconds. If the app uses preview images it could also. * The image at the `preview_img_url` url updates itself every couple of seconds. If the app uses preview images it could also.
* `started_at` is an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp. * `started_at` is an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp.
* `video_height`: `#!json 1080` = `HD`, `#!json < 1080` = `SD`, `#!json > 1080` = `UHD`. * `video_height`: `#!json 1080` = `HD`, `#!json < 1080` = `SD`, `#!json > 1080` = `UHD`.
* `mature`: If the stream is meant for mature audiences (set by the streamer themselves as a guideline). * `mature`: If the stream is meant for mature audiences (set by the streamer themselves as a guideline).
\ No newline at end of file
...@@ -4,9 +4,12 @@ go 1.13 ...@@ -4,9 +4,12 @@ go 1.13
require ( require (
github.com/JamesStewy/sse v0.3.0 github.com/JamesStewy/sse v0.3.0
github.com/dyson/certman v0.2.1
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/go-chi/chi v4.0.2+incompatible github.com/go-chi/chi v4.0.2+incompatible
github.com/hashicorp/go-multierror v1.0.0 github.com/hashicorp/go-multierror v1.0.0
github.com/markbates/pkger v0.12.8 github.com/markbates/pkger v0.12.8
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.7.0
github.com/urfave/cli v1.21.0 github.com/urfave/cli v1.21.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
) )
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/JamesStewy/sse v0.3.0 h1:1CABzcQydehMc54Vypki6b5+/WMmGZVWi7owRvDKo7g= github.com/JamesStewy/sse v0.3.0 h1:1CABzcQydehMc54Vypki6b5+/WMmGZVWi7owRvDKo7g=
github.com/JamesStewy/sse v0.3.0/go.mod h1:i60+CezIhaOZYviCbCRqXCDB7cVCWvUqlD2AYnxPVRE= github.com/JamesStewy/sse v0.3.0/go.mod h1:i60+CezIhaOZYviCbCRqXCDB7cVCWvUqlD2AYnxPVRE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dyson/certman v0.2.1 h1:+EJdgffbfwIkBvnyx97mfS3slfn2o4UN/LWGm+mWVrQ=
github.com/dyson/certman v0.2.1/go.mod h1:Z2ho3wmP4oCGON+c/RF+FJVsMb9zYZVsupp0c1a+SlQ=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
...@@ -17,17 +57,77 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= ...@@ -17,17 +57,77 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/markbates/pkger v0.12.8 h1:4mEUzWb1HzRnxPwUevBX8g8ntsQ4rWw2R8CRB2QdZVI= github.com/markbates/pkger v0.12.8 h1:4mEUzWb1HzRnxPwUevBX8g8ntsQ4rWw2R8CRB2QdZVI=
github.com/markbates/pkger v0.12.8/go.mod h1:C7e5A6bnWZT+nXkUwkvysGW7sxl/IGd63HEa6N/JY8s= github.com/markbates/pkger v0.12.8/go.mod h1:C7e5A6bnWZT+nXkUwkvysGW7sxl/IGd63HEa6N/JY8s=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U=
github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=