mirror of https://github.com/mpolden/echoip
Cache in Redis
- [x] Pipeline to update binary on server when changes are pushed - [x] Added config option for Cache TTL in Seconds in `/etc/echoip/config.toml - [x] Updated Readme for Cache Options - [x] Added `Null` cache for optional no use of cache - [x] Adding Redis Cache - [x] Moving configuration to file config - [x] Automatic Release for amd64 linux - [x] Adding automatic deployment - Added `LICENSE` file to the release file
This commit is contained in:
parent
9971835402
commit
79c8f54d44
|
@ -0,0 +1,32 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.18.x"
|
||||
|
||||
- name: Build EchoIP binary
|
||||
run: go build -o ./echoip ./cmd/echoip/main.go
|
||||
|
||||
- name: Upload Release Artifact
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
zip -r echoip-linux-amd64-${{ github.ref_name }}.zip echoip html LICENSE
|
||||
|
||||
- name: Create Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: echoip-linux-amd64-${{ github.ref_name }}.zip
|
||||
bodyFile: "CHANGELOG.md"
|
|
@ -1,4 +1,4 @@
|
|||
name: Go
|
||||
name: Test
|
||||
|
||||
on: [push]
|
||||
|
||||
|
@ -13,10 +13,7 @@ jobs:
|
|||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.13.x"
|
||||
|
||||
- name: Display Go version
|
||||
run: go version
|
||||
go-version: "1.18.x"
|
||||
|
||||
- name: Run tests
|
||||
run: go test ./...
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# Changelog
|
||||
|
||||
## 1.0.0 (2023-10-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Adding test to main.go ([a4d13ad](https://github.com/levelsoftware/echoip/commit/a4d13ad0edcc3c9dd591ad1aa26d2b50b8528168))
|
||||
* Remove if inside loop from String() ([08fdacc](https://github.com/levelsoftware/echoip/commit/08fdacc08254a53169a19ff7f14cfad4c70655c1))
|
8
Makefile
8
Makefile
|
@ -19,11 +19,15 @@ vet:
|
|||
check-fmt:
|
||||
bash -c "diff --line-format='%L' <(echo -n) <(gofmt -d -s .)"
|
||||
|
||||
|
||||
lint: check-fmt vet
|
||||
|
||||
install:
|
||||
install: install-config
|
||||
go install ./...
|
||||
|
||||
install-config:
|
||||
sudo install -D etc/echoip/config.toml /etc/echoip/config.toml
|
||||
|
||||
databases := GeoLite2-City GeoLite2-Country GeoLite2-ASN
|
||||
|
||||
$(databases):
|
||||
|
@ -63,7 +67,7 @@ docker-push: docker-test docker-login
|
|||
docker-pushx: docker-multiarch-builder docker-test docker-login
|
||||
$(DOCKER) buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t $(DOCKER_IMAGE) --push .
|
||||
|
||||
xinstall:
|
||||
xinstall: install-config
|
||||
env GOOS=$(XGOOS) GOARCH=$(XGOARCH) go install ./...
|
||||
|
||||
publish:
|
||||
|
|
76
README.md
76
README.md
|
@ -84,34 +84,58 @@ between IPv4 and IPv6 lookup.
|
|||
- Port testing
|
||||
- All endpoints (except `/port`) can return information about a custom IP address specified via `?ip=` query parameter
|
||||
- Open source under the [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause)
|
||||
- Supports IP Stack API or GeoIP
|
||||
|
||||
### Installation from Release
|
||||
|
||||
- Download release file.
|
||||
- Install `./echoip` binary ( `sudo install echoip /usr/local/bin/echoip` )
|
||||
- Install configuration file( `sudo install -D etc/echoip/config.toml /etc/echoip/config.toml` )
|
||||
- Point `config.TemplateDir` to release `html/`
|
||||
|
||||
### Installation from Source
|
||||
|
||||
- Install Go 1.18
|
||||
- `$ cd echoip/`
|
||||
- `$ make install`
|
||||
|
||||
### Usage
|
||||
|
||||
```
|
||||
$ echoip -h
|
||||
Usage of echoip:
|
||||
-C int
|
||||
Size of response cache. Set to 0 to disable
|
||||
-H value
|
||||
Header to trust for remote IP, if present (e.g. X-Real-IP)
|
||||
-P Enables profiling handlers
|
||||
-S string
|
||||
IP Stack API Key
|
||||
-a string
|
||||
Path to GeoIP ASN database
|
||||
-c string
|
||||
Path to GeoIP city database
|
||||
-d string
|
||||
Which database to use, 'ipstack' or 'geoip' (default "geoip")
|
||||
-f string
|
||||
Path to GeoIP country database
|
||||
-h Use HTTPS for IP Stack ( only non-free accounts )
|
||||
-l string
|
||||
Listening address (default ":8080")
|
||||
-p Enable port lookup
|
||||
-r Perform reverse hostname lookups
|
||||
-s Show sponsor logo
|
||||
-t string
|
||||
Path to template dir (default "html")
|
||||
-x Enable security module for IP Stack ( must have security module, aka. non-free account. )
|
||||
$ echoip
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Configuration is managed in the `etc/echoip/config.toml` file. This file should be located in the `/etc` folder on your server ( /etc/echoip/config.toml ). If you have the project on your server, you can run `make install-config` to copy it the right location.
|
||||
|
||||
```toml
|
||||
Listen = ":8080"
|
||||
TemplateDir = "html" # The directory of the template files ( eg, index.html )
|
||||
RedisUrl = "redis://localhost:6379" # Redis Connection URL, leave blank for no Cache
|
||||
CacheTtl = 3600 # in seconds
|
||||
ReverseLookup = true
|
||||
PortLookup = true
|
||||
ShowSponsor = true
|
||||
Database = "ipstack" # use "IP Stack" or "GeoIP"
|
||||
TrustedHeaders = [] # Which header to trust, eg, `["X-Real-IP"]`
|
||||
Profile = false # enable debug / profiling
|
||||
|
||||
[IPStack]
|
||||
ApiKey = ""
|
||||
UseHttps = true
|
||||
EnableSecurity = true
|
||||
|
||||
[GeoIP]
|
||||
CountryFile = ""
|
||||
CityFile = ""
|
||||
AsnFile = ""
|
||||
```
|
||||
|
||||
### Caching with Redis
|
||||
|
||||
You can connect EchoIP to a Redis client to cache each request per IP. You can configure the life of the key in `config.CacheTtl`.
|
||||
|
||||
### Running with `systemd`
|
||||
|
||||
There is a systemd service file you can install in `/etc/systemd`.
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
parser "github.com/levelsoftware/echoip/iputil/paser"
|
||||
)
|
||||
|
||||
type CachedResponse struct {
|
||||
response *parser.Response
|
||||
}
|
||||
|
||||
func (cr *CachedResponse) Build(response parser.Response) CachedResponse {
|
||||
return CachedResponse{
|
||||
response: &response,
|
||||
}
|
||||
}
|
||||
|
||||
func (cr *CachedResponse) Get() parser.Response {
|
||||
return *cr.response
|
||||
}
|
||||
|
||||
func (cr *CachedResponse) IsSet() bool {
|
||||
return cr.response != nil
|
||||
}
|
||||
|
||||
type Cache interface {
|
||||
Get(ctx context.Context, ip string, cachedResponse *CachedResponse) error
|
||||
Set(ctx context.Context, ip string, response CachedResponse, cacheTtl int) error
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Null struct{}
|
||||
|
||||
func (nc *Null) Get(ctx context.Context, ip string, cachedResponse *CachedResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nc *Null) Set(ctx context.Context, ip string, response CachedResponse, cacheTtl int) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/cache/v9"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type Redis struct {
|
||||
cache *cache.Cache
|
||||
}
|
||||
|
||||
func RedisCache(url string) (Redis, error) {
|
||||
opts, err := redis.ParseURL(url)
|
||||
if err != nil {
|
||||
return Redis{}, err
|
||||
}
|
||||
rdb := redis.NewClient(opts)
|
||||
cache := cache.New(&cache.Options{
|
||||
Redis: rdb,
|
||||
LocalCache: cache.NewTinyLFU(1000, time.Minute),
|
||||
})
|
||||
return Redis{
|
||||
cache: cache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Redis) Get(ctx context.Context, ip string, cachedResponse *CachedResponse) error {
|
||||
return r.cache.Get(ctx, ip, cachedResponse)
|
||||
}
|
||||
|
||||
func (r *Redis) Set(ctx context.Context, ip string, response CachedResponse, cacheTtl int) error {
|
||||
return r.cache.Set(&cache.Item{
|
||||
Ctx: ctx,
|
||||
Key: ip,
|
||||
Value: response,
|
||||
TTL: time.Duration(cacheTtl * int(time.Second)),
|
||||
})
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/levelsoftware/echoip/cache"
|
||||
"github.com/levelsoftware/echoip/http"
|
||||
"github.com/levelsoftware/echoip/iputil"
|
||||
"github.com/levelsoftware/echoip/iputil/geo"
|
||||
|
@ -31,50 +33,77 @@ func init() {
|
|||
log.SetFlags(log.Lshortfile)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var ipstackApiKey string
|
||||
service := flag.String("d", "geoip", "Which database to use, 'ipstack' or 'geoip'")
|
||||
flag.StringVar(&ipstackApiKey, "S", "", "IP Stack API Key")
|
||||
ipStackEnableSecurityModule := flag.Bool("x", false, "Enable security module for IP Stack ( must have security module, aka. non-free account. )")
|
||||
ipStackUseHttps := flag.Bool("h", false, "Use HTTPS for IP Stack ( only non-free accounts )")
|
||||
countryFile := flag.String("f", "", "Path to GeoIP country database")
|
||||
cityFile := flag.String("c", "", "Path to GeoIP city database")
|
||||
asnFile := flag.String("a", "", "Path to GeoIP ASN database")
|
||||
listen := flag.String("l", ":8080", "Listening address")
|
||||
reverseLookup := flag.Bool("r", false, "Perform reverse hostname lookups")
|
||||
portLookup := flag.Bool("p", false, "Enable port lookup")
|
||||
template := flag.String("t", "html", "Path to template dir")
|
||||
cacheSize := flag.Int("C", 0, "Size of response cache. Set to 0 to disable")
|
||||
profile := flag.Bool("P", false, "Enables profiling handlers")
|
||||
sponsor := flag.Bool("s", false, "Show sponsor logo")
|
||||
var headers multiValueFlag
|
||||
flag.Var(&headers, "H", "Header to trust for remote IP, if present (e.g. X-Real-IP)")
|
||||
flag.Parse()
|
||||
type IPStack struct {
|
||||
ApiKey string
|
||||
UseHttps bool
|
||||
EnableSecurity bool
|
||||
}
|
||||
|
||||
if len(flag.Args()) != 0 {
|
||||
flag.Usage()
|
||||
return
|
||||
type GeoIP struct {
|
||||
CountryFile string
|
||||
CityFile string
|
||||
AsnFile string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Listen string
|
||||
TemplateDir string
|
||||
RedisUrl string
|
||||
CacheTtl int
|
||||
ReverseLookup bool
|
||||
PortLookup bool
|
||||
ShowSponsor bool
|
||||
TrustedHeaders []string
|
||||
|
||||
Database string
|
||||
Profile bool
|
||||
|
||||
IPStack IPStack
|
||||
GeoIP GeoIP
|
||||
}
|
||||
|
||||
func main() {
|
||||
file, err := os.Open("/etc/echoip/config.toml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var config Config
|
||||
|
||||
b, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = toml.Unmarshal(b, &config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var parser parser.Parser
|
||||
if *service == "geoip" {
|
||||
if config.Database == "geoip" {
|
||||
log.Print("Using GeoIP for IP database")
|
||||
geo, err := geo.Open(*countryFile, *cityFile, *asnFile)
|
||||
geo, err := geo.Open(
|
||||
config.GeoIP.CountryFile,
|
||||
config.GeoIP.CityFile,
|
||||
config.GeoIP.AsnFile,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
parser = &geo
|
||||
}
|
||||
|
||||
if *service == "ipstack" {
|
||||
if config.Database == "ipstack" {
|
||||
log.Print("Using GeoIP for IP database")
|
||||
if *ipStackEnableSecurityModule {
|
||||
if config.IPStack.EnableSecurity {
|
||||
log.Print("Enable Security Module ( Requires Professional Plus account )")
|
||||
}
|
||||
enableSecurity := ipstackApi.ParamEnableSecurity(*ipStackEnableSecurityModule)
|
||||
apiKey := ipstackApi.ParamToken(ipstackApiKey)
|
||||
useHttps := ipstackApi.ParamUseHTTPS(*ipStackUseHttps)
|
||||
if *ipStackUseHttps {
|
||||
enableSecurity := ipstackApi.ParamEnableSecurity(config.IPStack.EnableSecurity)
|
||||
apiKey := ipstackApi.ParamToken(config.IPStack.ApiKey)
|
||||
useHttps := ipstackApi.ParamUseHTTPS(config.IPStack.UseHttps)
|
||||
if config.IPStack.UseHttps {
|
||||
log.Print("Use IP Stack HTTPS API ( Requires non-free account )")
|
||||
}
|
||||
if err := ipstackApi.Init(apiKey, enableSecurity, useHttps); err != nil {
|
||||
|
@ -84,37 +113,45 @@ func main() {
|
|||
parser = &ips
|
||||
}
|
||||
|
||||
cache := http.NewCache(*cacheSize)
|
||||
server := http.New(parser, cache, *profile)
|
||||
server.IPHeaders = headers
|
||||
if _, err := os.Stat(*template); err == nil {
|
||||
server.Template = *template
|
||||
} else {
|
||||
log.Printf("Not configuring default handler: Template not found: %s", *template)
|
||||
var serverCache cache.Cache
|
||||
if len(config.RedisUrl) > 0 {
|
||||
redisCache, err := cache.RedisCache(config.RedisUrl)
|
||||
serverCache = &redisCache
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if *reverseLookup {
|
||||
} else {
|
||||
serverCache = &cache.Null{}
|
||||
}
|
||||
|
||||
server := http.New(parser, serverCache, config.CacheTtl, config.Profile)
|
||||
server.IPHeaders = config.TrustedHeaders
|
||||
|
||||
if _, err := os.Stat(config.TemplateDir); err == nil {
|
||||
server.Template = config.TemplateDir
|
||||
} else {
|
||||
log.Printf("Not configuring default handler: Template not found: %s", config.TemplateDir)
|
||||
}
|
||||
if config.ReverseLookup {
|
||||
log.Println("Enabling reverse lookup")
|
||||
server.LookupAddr = iputil.LookupAddr
|
||||
}
|
||||
if *portLookup {
|
||||
if config.PortLookup {
|
||||
log.Println("Enabling port lookup")
|
||||
server.LookupPort = iputil.LookupPort
|
||||
}
|
||||
if *sponsor {
|
||||
if config.ShowSponsor {
|
||||
log.Println("Enabling sponsor logo")
|
||||
server.Sponsor = *sponsor
|
||||
server.Sponsor = config.ShowSponsor
|
||||
}
|
||||
if len(headers) > 0 {
|
||||
log.Printf("Trusting remote IP from header(s): %s", headers.String())
|
||||
if len(config.TrustedHeaders) > 0 {
|
||||
log.Printf("Trusting remote IP from header(s): %s", config.TrustedHeaders)
|
||||
}
|
||||
if *cacheSize > 0 {
|
||||
log.Printf("Cache capacity set to %d", *cacheSize)
|
||||
}
|
||||
if *profile {
|
||||
if config.Profile {
|
||||
log.Printf("Enabling profiling handlers")
|
||||
}
|
||||
log.Printf("Listening on http://%s", *listen)
|
||||
if err := server.ListenAndServe(*listen); err != nil {
|
||||
log.Printf("Listening on http://%s", config.Listen)
|
||||
if err := server.ListenAndServe(config.Listen); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
cache:
|
||||
image: redis:6.2-alpine
|
||||
restart: always
|
||||
ports:
|
||||
- '6379:6379'
|
||||
command: redis-server --save 20 1 --loglevel warning
|
||||
volumes:
|
||||
- cache:/data
|
||||
|
||||
volumes:
|
||||
cache:
|
||||
driver: local
|
|
@ -0,0 +1,20 @@
|
|||
Listen = ":8080"
|
||||
TemplateDir = "html/index.html"
|
||||
RedisUrl = "redis://localhost:6379"
|
||||
CacheTtl = 3600 # in seconds
|
||||
ReverseLookup = true
|
||||
PortLookup = true
|
||||
ShowSponsor = true
|
||||
Database = "ipstack" # use "IP Stack" or "GeoIP"
|
||||
TrustedHeaders = []
|
||||
Profile = false # enable debug / profiling
|
||||
|
||||
[IPStack]
|
||||
ApiKey = "HelloWorld"
|
||||
UseHttps = true
|
||||
EnableSecurity = true
|
||||
|
||||
[GeoIP]
|
||||
CountryFile = ""
|
||||
CityFile = ""
|
||||
AsnFile = ""
|
|
@ -0,0 +1,17 @@
|
|||
[Unit]
|
||||
Description=EchoIP
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=web
|
||||
|
||||
ExecStart=/usr/local/bin/echoip
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=echoip
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
18
go.mod
18
go.mod
|
@ -1,9 +1,23 @@
|
|||
module github.com/levelsoftware/echoip
|
||||
|
||||
go 1.13
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
github.com/go-redis/cache/v9 v9.0.0
|
||||
github.com/oschwald/geoip2-golang v1.5.0
|
||||
github.com/qioalice/ipstack v1.0.1
|
||||
golang.org/x/sys v0.0.0-20210223212115-eede4237b368 // indirect
|
||||
github.com/redis/go-redis/v9 v9.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.8.0 // indirect
|
||||
github.com/vmihailenco/go-tinylfu v0.2.2 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
)
|
||||
|
|
203
go.sum
203
go.sum
|
@ -1,5 +1,78 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
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-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0=
|
||||
github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/protobuf v1.2.0/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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||
github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
|
||||
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
|
||||
github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
|
||||
github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
|
||||
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
||||
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
|
||||
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
|
||||
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
|
||||
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
|
||||
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
|
||||
github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=
|
||||
github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
|
||||
|
@ -8,14 +81,132 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/qioalice/ipstack v1.0.1 h1:Ync2O+tR2AH9/TzTg4aeJxd9c1Xz2CeH1joECwnhvms=
|
||||
github.com/qioalice/ipstack v1.0.1/go.mod h1:6eB9LdNCUdUoOsfDB8Pn2GpmD2I+f2k3yR30ceuf/rY=
|
||||
github.com/redis/go-redis/v9 v9.0.0-rc.4/go.mod h1:Vo3EsyWnicKnSKCA7HhgnvnyA74wOA69Cd2Meli5mmA=
|
||||
github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg=
|
||||
github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI=
|
||||
github.com/vmihailenco/go-tinylfu v0.2.2/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210223212115-eede4237b368 h1:fDE3p0qf2V1co1vfj3/o87Ps8Hq6QTGNxJ5Xe7xSp80=
|
||||
golang.org/x/sys v0.0.0-20210223212115-eede4237b368/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
101
http/cache.go
101
http/cache.go
|
@ -1,101 +0,0 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
parser "github.com/levelsoftware/echoip/iputil/paser"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
capacity int
|
||||
mu sync.RWMutex
|
||||
entries map[uint64]*list.Element
|
||||
values *list.List
|
||||
evictions uint64
|
||||
}
|
||||
|
||||
type CacheStats struct {
|
||||
Capacity int
|
||||
Size int
|
||||
Evictions uint64
|
||||
}
|
||||
|
||||
func NewCache(capacity int) *Cache {
|
||||
if capacity < 0 {
|
||||
capacity = 0
|
||||
}
|
||||
return &Cache{
|
||||
capacity: capacity,
|
||||
entries: make(map[uint64]*list.Element),
|
||||
values: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
func key(ip net.IP) uint64 {
|
||||
h := fnv.New64a()
|
||||
h.Write(ip)
|
||||
return h.Sum64()
|
||||
}
|
||||
|
||||
func (c *Cache) Set(ip net.IP, resp parser.Response) {
|
||||
if c.capacity == 0 {
|
||||
return
|
||||
}
|
||||
k := key(ip)
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
minEvictions := len(c.entries) - c.capacity + 1
|
||||
if minEvictions > 0 { // At or above capacity. Shrink the cache
|
||||
evicted := 0
|
||||
for el := c.values.Front(); el != nil && evicted < minEvictions; {
|
||||
value := el.Value.(parser.Response)
|
||||
delete(c.entries, key(value.IP))
|
||||
next := el.Next()
|
||||
c.values.Remove(el)
|
||||
el = next
|
||||
evicted++
|
||||
}
|
||||
c.evictions += uint64(evicted)
|
||||
}
|
||||
current, ok := c.entries[k]
|
||||
if ok {
|
||||
c.values.Remove(current)
|
||||
}
|
||||
c.entries[k] = c.values.PushBack(resp)
|
||||
}
|
||||
|
||||
func (c *Cache) Get(ip net.IP) (parser.Response, bool) {
|
||||
k := key(ip)
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
r, ok := c.entries[k]
|
||||
if !ok {
|
||||
return parser.Response{}, false
|
||||
}
|
||||
return r.Value.(parser.Response), true
|
||||
}
|
||||
|
||||
func (c *Cache) Resize(capacity int) error {
|
||||
if capacity < 0 {
|
||||
return fmt.Errorf("invalid capacity: %d\n", capacity)
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.capacity = capacity
|
||||
c.evictions = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) Stats() CacheStats {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return CacheStats{
|
||||
Size: len(c.entries),
|
||||
Capacity: c.capacity,
|
||||
Evictions: c.evictions,
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
parser "github.com/levelsoftware/echoip/iputil/paser"
|
||||
)
|
||||
|
||||
func TestCacheCapacity(t *testing.T) {
|
||||
var tests = []struct {
|
||||
addCount, capacity, size int
|
||||
evictions uint64
|
||||
}{
|
||||
{1, 0, 0, 0},
|
||||
{1, 2, 1, 0},
|
||||
{2, 2, 2, 0},
|
||||
{3, 2, 2, 1},
|
||||
{10, 5, 5, 5},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
c := NewCache(tt.capacity)
|
||||
var responses []parser.Response
|
||||
for i := 0; i < tt.addCount; i++ {
|
||||
ip := net.ParseIP(fmt.Sprintf("192.0.2.%d", i))
|
||||
r := parser.Response{IP: ip}
|
||||
responses = append(responses, r)
|
||||
c.Set(ip, r)
|
||||
}
|
||||
if got := len(c.entries); got != tt.size {
|
||||
t.Errorf("#%d: len(entries) = %d, want %d", i, got, tt.size)
|
||||
}
|
||||
if got := c.evictions; got != tt.evictions {
|
||||
t.Errorf("#%d: evictions = %d, want %d", i, got, tt.evictions)
|
||||
}
|
||||
if tt.capacity > 0 && tt.addCount > tt.capacity && tt.capacity == tt.size {
|
||||
lastAdded := responses[tt.addCount-1]
|
||||
if _, ok := c.Get(lastAdded.IP); !ok {
|
||||
t.Errorf("#%d: Get(%s) = (_, %t), want (_, %t)", i, lastAdded.IP.String(), ok, !ok)
|
||||
}
|
||||
firstAdded := responses[0]
|
||||
if _, ok := c.Get(firstAdded.IP); ok {
|
||||
t.Errorf("#%d: Get(%s) = (_, %t), want (_, %t)", i, firstAdded.IP.String(), ok, !ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheDuplicate(t *testing.T) {
|
||||
c := NewCache(10)
|
||||
ip := net.ParseIP("192.0.2.1")
|
||||
response := parser.Response{IP: ip}
|
||||
c.Set(ip, response)
|
||||
c.Set(ip, response)
|
||||
want := 1
|
||||
if got := len(c.entries); got != want {
|
||||
t.Errorf("want %d entries, got %d", want, got)
|
||||
}
|
||||
if got := c.values.Len(); got != want {
|
||||
t.Errorf("want %d values, got %d", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheResize(t *testing.T) {
|
||||
c := NewCache(10)
|
||||
for i := 1; i <= 20; i++ {
|
||||
ip := net.ParseIP(fmt.Sprintf("192.0.2.%d", i))
|
||||
r := parser.Response{IP: ip}
|
||||
c.Set(ip, r)
|
||||
}
|
||||
if got, want := len(c.entries), 10; got != want {
|
||||
t.Errorf("want %d entries, got %d", want, got)
|
||||
}
|
||||
if got, want := c.evictions, uint64(10); got != want {
|
||||
t.Errorf("want %d evictions, got %d", want, got)
|
||||
}
|
||||
if err := c.Resize(5); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := c.evictions, uint64(0); got != want {
|
||||
t.Errorf("want %d evictions, got %d", want, got)
|
||||
}
|
||||
r := parser.Response{IP: net.ParseIP("192.0.2.42")}
|
||||
c.Set(r.IP, r)
|
||||
if got, want := len(c.entries), 5; got != want {
|
||||
t.Errorf("want %d entries, got %d", want, got)
|
||||
}
|
||||
}
|
83
http/http.go
83
http/http.go
|
@ -1,16 +1,18 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"net/http/pprof"
|
||||
|
||||
rcache "github.com/go-redis/cache/v9"
|
||||
"github.com/levelsoftware/echoip/cache"
|
||||
parser "github.com/levelsoftware/echoip/iputil/paser"
|
||||
"github.com/levelsoftware/echoip/useragent"
|
||||
|
||||
|
@ -29,7 +31,8 @@ type Server struct {
|
|||
IPHeaders []string
|
||||
LookupAddr func(net.IP) (string, error)
|
||||
LookupPort func(net.IP, uint64) error
|
||||
cache *Cache
|
||||
cache cache.Cache
|
||||
cacheTtl int
|
||||
parser parser.Parser
|
||||
profile bool
|
||||
Sponsor bool
|
||||
|
@ -41,8 +44,8 @@ type PortResponse struct {
|
|||
Reachable bool `json:"reachable"`
|
||||
}
|
||||
|
||||
func New(parser parser.Parser, cache *Cache, profile bool) *Server {
|
||||
return &Server{cache: cache, parser: parser, profile: profile}
|
||||
func New(parser parser.Parser, cache cache.Cache, cacheTtl int, profile bool) *Server {
|
||||
return &Server{cache: cache, cacheTtl: cacheTtl, parser: parser, profile: profile}
|
||||
}
|
||||
|
||||
func ipFromForwardedForHeader(v string) string {
|
||||
|
@ -101,24 +104,38 @@ func userAgentFromRequest(r *http.Request) *useragent.UserAgent {
|
|||
}
|
||||
|
||||
func (s *Server) newResponse(r *http.Request) (parser.Response, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
ip, err := ipFromRequest(s.IPHeaders, r, true)
|
||||
if err != nil {
|
||||
return parser.Response{}, err
|
||||
}
|
||||
response, ok := s.cache.Get(ip)
|
||||
if ok {
|
||||
// Do not cache user agent
|
||||
response.UserAgent = userAgentFromRequest(r)
|
||||
return response, nil
|
||||
|
||||
var cachedResponse cache.CachedResponse
|
||||
if err := s.cache.Get(ctx, ip.String(), &cachedResponse); err != nil && err != rcache.ErrCacheMiss {
|
||||
return parser.Response{}, err
|
||||
}
|
||||
|
||||
if cachedResponse.IsSet() {
|
||||
log.Printf("Return cached response for %s", ip.String())
|
||||
return cachedResponse.Get(), nil
|
||||
}
|
||||
|
||||
var hostname string
|
||||
if s.LookupAddr != nil {
|
||||
hostname, _ = s.LookupAddr(ip)
|
||||
}
|
||||
|
||||
var response parser.Response
|
||||
response, err = s.parser.Parse(ip, hostname)
|
||||
s.cache.Set(ip, response)
|
||||
|
||||
log.Printf("Caching response for %s", ip.String())
|
||||
if err := s.cache.Set(ctx, ip.String(), cachedResponse.Build(response), s.cacheTtl); err != nil {
|
||||
return parser.Response{}, err
|
||||
}
|
||||
|
||||
response.UserAgent = userAgentFromRequest(r)
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -237,50 +254,6 @@ func (s *Server) PortHandler(w http.ResponseWriter, r *http.Request) *appError {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) cacheResizeHandler(w http.ResponseWriter, r *http.Request) *appError {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return badRequest(err).WithMessage(err.Error()).AsJSON()
|
||||
}
|
||||
capacity, err := strconv.Atoi(string(body))
|
||||
if err != nil {
|
||||
return badRequest(err).WithMessage(err.Error()).AsJSON()
|
||||
}
|
||||
if err := s.cache.Resize(capacity); err != nil {
|
||||
return badRequest(err).WithMessage(err.Error()).AsJSON()
|
||||
}
|
||||
data := struct {
|
||||
Message string `json:"message"`
|
||||
}{fmt.Sprintf("Changed cache capacity to %d.", capacity)}
|
||||
b, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return internalServerError(err).AsJSON()
|
||||
}
|
||||
w.Header().Set("Content-Type", jsonMediaType)
|
||||
w.Write(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) cacheHandler(w http.ResponseWriter, r *http.Request) *appError {
|
||||
cacheStats := s.cache.Stats()
|
||||
var data = struct {
|
||||
Size int `json:"size"`
|
||||
Capacity int `json:"capacity"`
|
||||
Evictions uint64 `json:"evictions"`
|
||||
}{
|
||||
cacheStats.Size,
|
||||
cacheStats.Capacity,
|
||||
cacheStats.Evictions,
|
||||
}
|
||||
b, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return internalServerError(err).AsJSON()
|
||||
}
|
||||
w.Header().Set("Content-Type", jsonMediaType)
|
||||
w.Write(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) DefaultHandler(w http.ResponseWriter, r *http.Request) *appError {
|
||||
response, err := s.newResponse(r)
|
||||
if err != nil {
|
||||
|
@ -411,8 +384,6 @@ func (s *Server) Handler() http.Handler {
|
|||
|
||||
// Profiling
|
||||
if s.profile {
|
||||
r.Route("POST", "/debug/cache/resize", s.cacheResizeHandler)
|
||||
r.Route("GET", "/debug/cache/", s.cacheHandler)
|
||||
r.Route("GET", "/debug/pprof/cmdline", wrapHandlerFunc(pprof.Cmdline))
|
||||
r.Route("GET", "/debug/pprof/profile", wrapHandlerFunc(pprof.Profile))
|
||||
r.Route("GET", "/debug/pprof/symbol", wrapHandlerFunc(pprof.Symbol))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
@ -11,6 +12,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/levelsoftware/echoip/cache"
|
||||
"github.com/levelsoftware/echoip/iputil"
|
||||
"github.com/levelsoftware/echoip/iputil/geo"
|
||||
parser "github.com/levelsoftware/echoip/iputil/paser"
|
||||
|
@ -66,8 +68,23 @@ func (t *testDb) Parse(ip net.IP, hostname string) (parser.Response, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
type FakeCache struct{}
|
||||
|
||||
var cachedResponse cache.CachedResponse
|
||||
|
||||
func (fc *FakeCache) Get(ctx context.Context, ip string, response *cache.CachedResponse) error {
|
||||
response = &cachedResponse
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fc *FakeCache) Set(ctx context.Context, ip string, response cache.CachedResponse, cacheTtl int) error {
|
||||
cachedResponse = response
|
||||
return nil
|
||||
}
|
||||
|
||||
func testServer() *Server {
|
||||
return &Server{cache: NewCache(100), parser: &testDb{}, LookupAddr: lookupAddr, LookupPort: lookupPort}
|
||||
fakeCache := FakeCache{}
|
||||
return &Server{cache: &fakeCache, cacheTtl: 100, parser: &testDb{}, LookupAddr: lookupAddr, LookupPort: lookupPort}
|
||||
}
|
||||
|
||||
func httpGet(url string, acceptMediaType string, userAgent string) (string, int, error) {
|
||||
|
@ -214,36 +231,6 @@ func TestJSONHandlers(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCacheHandler(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
srv := testServer()
|
||||
srv.profile = true
|
||||
s := httptest.NewServer(srv.Handler())
|
||||
got, _, err := httpGet(s.URL+"/debug/cache/", jsonMediaType, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := "{\n \"size\": 0,\n \"capacity\": 100,\n \"evictions\": 0\n}"
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheResizeHandler(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
srv := testServer()
|
||||
srv.profile = true
|
||||
s := httptest.NewServer(srv.Handler())
|
||||
_, got, err := httpPost(s.URL+"/debug/cache/resize", "10")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := "{\n \"message\": \"Changed cache capacity to 10.\"\n}"
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPFromRequest(t *testing.T) {
|
||||
var tests = []struct {
|
||||
remoteAddr string
|
||||
|
|
|
@ -62,7 +62,10 @@ func (ips *IPStack) ParseSecurityResponse(parserResponse *parser.Response) {
|
|||
CrawlerName: ips.response.Security.CrawlerName,
|
||||
CrawlerType: ips.response.Security.CrawlerType,
|
||||
ThreatLevel: ips.response.Security.ThreatLevel,
|
||||
ThreatTypes: ips.response.Security.ThreatTypes.([]string),
|
||||
}
|
||||
|
||||
if threat_types, ok := ips.response.Security.ThreatTypes.([]string); ok {
|
||||
parserResponse.Security.ThreatTypes = threat_types
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue