Skip to content

Commit

Permalink
refactor: v2 api, openapi, native bulk support, performance improveme…
Browse files Browse the repository at this point in the history
…nts, deprecations and more

Signed-off-by: Liam Stanley <[email protected]>
  • Loading branch information
lrstanley committed Aug 30, 2022
1 parent b6cf62b commit d86a79a
Show file tree
Hide file tree
Showing 63 changed files with 2,507 additions and 793 deletions.
16 changes: 10 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/cors v1.2.1
github.com/go-chi/httprate v0.5.3
github.com/lrstanley/chix v0.0.0-20220730235500-2ed7ac07e5df
github.com/lrstanley/chix v0.0.0-20220828011827-510c3c57774f
github.com/lrstanley/clix v0.0.0-20220711131806-9a1b44c031c8
github.com/lrstanley/go-bogon v0.0.0-20220507183221-362a880cf97b
github.com/oschwald/maxminddb-golang v1.9.0
Expand All @@ -31,16 +31,20 @@ require (
github.com/joho/godotenv v1.4.0 // indirect
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lrstanley/go-sempool v0.0.0-20220507183223-1b08ce19f0b9 // indirect
github.com/markbates/goth v1.73.0 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/puzpuzpuz/xsync v1.4.3 // indirect
github.com/sethvargo/go-limiter v0.7.2 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 // indirect
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.0.0-20220730100132-1609e554cd39 // indirect
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 // indirect
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
Expand Down
24 changes: 24 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,18 @@ github.com/lrstanley/chix v0.0.0-20220727021022-5e94e996e155 h1:ctPtyv1PwIPnCLIJ
github.com/lrstanley/chix v0.0.0-20220727021022-5e94e996e155/go.mod h1:UxmCvkoFWQP+3c3bVEGmAwHz9d7MHBX1IwfQK3i9e7U=
github.com/lrstanley/chix v0.0.0-20220730235500-2ed7ac07e5df h1:SNHYkrShzrG1J/4fkWcnkar5wkGnGQ418U9ZweU0b7c=
github.com/lrstanley/chix v0.0.0-20220730235500-2ed7ac07e5df/go.mod h1:UxmCvkoFWQP+3c3bVEGmAwHz9d7MHBX1IwfQK3i9e7U=
github.com/lrstanley/chix v0.0.0-20220827154534-2a6495a010ba h1:MWCsOgEp3yqmlott7DyYbA3pdSHGUEJV+MPxACjYVy0=
github.com/lrstanley/chix v0.0.0-20220827154534-2a6495a010ba/go.mod h1:UxmCvkoFWQP+3c3bVEGmAwHz9d7MHBX1IwfQK3i9e7U=
github.com/lrstanley/chix v0.0.0-20220827155306-914333efee82 h1:yl2skF5MkAcBQ8k3bqS178PrYvvQCveJE6IyZRfzqxY=
github.com/lrstanley/chix v0.0.0-20220827155306-914333efee82/go.mod h1:UxmCvkoFWQP+3c3bVEGmAwHz9d7MHBX1IwfQK3i9e7U=
github.com/lrstanley/chix v0.0.0-20220828011827-510c3c57774f h1:u1bGPKcOZs9nCGNI5n1zVNfIlLduSHwWhC3d6A0kFE0=
github.com/lrstanley/chix v0.0.0-20220828011827-510c3c57774f/go.mod h1:UxmCvkoFWQP+3c3bVEGmAwHz9d7MHBX1IwfQK3i9e7U=
github.com/lrstanley/clix v0.0.0-20220711131806-9a1b44c031c8 h1:3wsR1rHuFfZTKV9vlG7DUYAKIJzxEnopLmCAXwEH/uo=
github.com/lrstanley/clix v0.0.0-20220711131806-9a1b44c031c8/go.mod h1:bh1gunV7PU+0D+RpNZDFToQEWxtmR6U8vvG2X3yCTKs=
github.com/lrstanley/go-bogon v0.0.0-20220507183221-362a880cf97b h1:jFRbU7IgKGjXlo1ERztns+QOlVRExiP0syyVIsP1TqU=
github.com/lrstanley/go-bogon v0.0.0-20220507183221-362a880cf97b/go.mod h1:1H1sGTRZ05IO1sQHKLAQQ34v19KrQeYg2Ix9HgJuFXQ=
github.com/lrstanley/go-sempool v0.0.0-20220507183223-1b08ce19f0b9 h1:qKv/8KvlUVpek/oWs/CET8oCRFxLFvKLl0naoES1dLI=
github.com/lrstanley/go-sempool v0.0.0-20220507183223-1b08ce19f0b9/go.mod h1:P02r0Kf+TlgNvBMPfFQglsEPJ13ooIIKR+747c/nlCo=
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
github.com/markbates/goth v1.73.0 h1:X5QUUHLP5puJ4dhoPKkV3PhDIvvQEzsfVxsUmDNSJ28=
github.com/markbates/goth v1.73.0/go.mod h1:X6xdNgpapSENS0O35iTBBcMHoJDQDfI9bJl+APCkYMc=
Expand All @@ -215,11 +223,15 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/puzpuzpuz/xsync v1.4.3 h1:nS/Iqc4EnpJ8jm/MzJ+e3MUaP2Ys2mqXeEfoxoU0HaM=
github.com/puzpuzpuz/xsync v1.4.3/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg=
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sethvargo/go-limiter v0.7.2 h1:FgC4N7RMpV5gMrUdda15FaFTkQ/L4fEqM7seXMs4oO8=
github.com/sethvargo/go-limiter v0.7.2/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiwf72uGu0CXCcU=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
Expand Down Expand Up @@ -262,6 +274,8 @@ golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand All @@ -272,6 +286,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down Expand Up @@ -328,6 +344,8 @@ golang.org/x/net v0.0.0-20220726230323-06994584191e h1:wOQNKh1uuDGRnmgF0jDxh7ctg
golang.org/x/net v0.0.0-20220726230323-06994584191e/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 h1:UreQrH7DbFXSi9ZFox6FNT3WBooWmdANpU+IfkT1T4I=
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand All @@ -338,6 +356,8 @@ golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 h1:zwrSfklXn0gxyLRX/aR+q6
golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -350,6 +370,8 @@ golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -391,6 +413,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9w
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220730100132-1609e554cd39 h1:aNCnH+Fiqs7ZDTFH6oEFjIfbX2HvgQXJ6uQuUbTobjk=
golang.org/x/sys v0.0.0-20220730100132-1609e554cd39/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/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/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
20 changes: 10 additions & 10 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/go-chi/httprate"
"github.com/lrstanley/chix"
"github.com/lrstanley/geoip/internal/handlers/apihandler"
"github.com/lrstanley/geoip/internal/httpware"
Expand All @@ -27,6 +26,8 @@ var staticFS embed.FS
func httpServer(ctx context.Context) *http.Server {
r := chi.NewRouter()

limiter := httpware.NewLimiter(cli.Flags.HTTP, 1*time.Hour)

if len(cli.Flags.HTTP.TrustedProxies) > 0 {
r.Use(chix.UseRealIP(cli.Flags.HTTP.TrustedProxies, chix.OptUseXForwardedFor))
}
Expand All @@ -37,13 +38,11 @@ func httpServer(ctx context.Context) *http.Server {
middleware.RequestID,
chix.UseStructuredLogger(logger),
chix.UseDebug(cli.Debug),
middleware.Recoverer,
chix.Recoverer,
middleware.Maybe(middleware.StripSlashes, func(r *http.Request) bool {
return !strings.HasPrefix(r.URL.Path, "/debug/")
}),
middleware.Compress(5),
httpware.UseMetadata(lookupSvc),
httpware.UseLanguage(lookupSvc),
)

if cli.Flags.HTTP.MaxConcurrent > 0 {
Expand Down Expand Up @@ -74,18 +73,19 @@ func httpServer(ctx context.Context) *http.Server {
// "Content-Security-Policy",
// "default-src 'self'; media-src * data:; style-src 'self' 'unsafe-inline'; object-src 'none'; child-src 'none'; frame-src 'none'; worker-src 'none'",
// ),
middleware.SetHeader("X-Frame-Options", "DENY"),
middleware.SetHeader("X-Content-Type-Options", "nosniff"),
middleware.SetHeader("Referrer-Policy", "no-referrer-when-downgrade"),
middleware.SetHeader("Permissions-Policy", "clipboard-write=(self)"),
httprate.LimitByIP(cli.Flags.HTTP.Limit, 1*time.Hour),
chix.UseHeaders(map[string]string{
"X-Frame-Options": "DENY",
"X-Content-Type-Options": "nosniff",
"Referrer-Policy": "no-referrer-when-downgrade",
"Permissions-Policy": "clipboard-write=(self)",
}),
)

if cli.Debug {
r.With(chix.UsePrivateIP).Mount("/debug", middleware.Profiler())
}

r.Route("/api", apihandler.New(lookupSvc).Route)
r.Route("/api", apihandler.New(lookupSvc, limiter).Route)

r.NotFound(chix.UseStatic(ctx, &chix.Static{
FS: staticFS,
Expand Down
116 changes: 116 additions & 0 deletions internal/handlers/apihandler/bulk_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) Liam Stanley <[email protected]>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package apihandler

import (
"net/http"
"sync"

"github.com/lrstanley/chix"
"github.com/lrstanley/geoip/internal/httpware"
"github.com/lrstanley/geoip/internal/models"
"github.com/lrstanley/go-sempool"
"golang.org/x/exp/slices"
)

type BulkRequest struct {
models.LookupOptions

Addresses []string `form:"addresses" json:"addresses" validate:"required,min=1,max=25,dive,hostname|ip|fqdn,required"`
}

type BulkError struct {
Query string `json:"query"`
Error string `json:"error"`
}

type BulkResponse struct {
mu sync.Mutex
Results []*models.Response `json:"results"`
Errors []*BulkError `json:"errors"`
}

func (b *BulkResponse) AddResult(result *models.Response) {
b.mu.Lock()
b.Results = append(b.Results, result)
b.mu.Unlock()
}

func (b *BulkResponse) AddError(query string, err error) {
b.mu.Lock()
if models.IsClientError(err) {
b.Errors = append(b.Errors, &BulkError{
Query: query,
Error: err.Error(),
})
} else {
b.Errors = append(b.Errors, &BulkError{
Query: query,
Error: "Internal server error",
})
}
b.mu.Unlock()
}

func (h *handler) postBulkV2(w http.ResponseWriter, r *http.Request) {
opts := &BulkRequest{}

// Disable host lookup by default for bulk requests, to reduce the amount
// of dns requests needed, and improve response time.
opts.LookupOptions.DisableHostLookup = true

if err := chix.Bind(r, opts); err != nil {
chix.Error(w, r, err)
return
}

// Sort, and remove duplicates.
slices.Sort(opts.Addresses)
opts.Addresses = slices.Compact(opts.Addresses)

if len(opts.Languages) == 0 {
opts.Languages = httpware.GetLanguage(r)
}

// Max 5 concurrent lookups from this request.
pool := sempool.New(5)

resp := &BulkResponse{
Results: make([]*models.Response, 0, len(opts.Addresses)),
Errors: []*BulkError{},
}

for _, a := range opts.Addresses {
_, _, _, ok, err := h.limiter.Store.Take(r.Context(), h.limiter.Key(r))
if err != nil {
resp.AddError(a, err)
continue
}

if !ok {
resp.AddError(a, &models.ErrRateLimitExceeded{Address: a})
continue
}

pool.Slot()

go func(addr string) {
defer pool.Free()

result, err := h.lookupSvc.Lookup(r.Context(), addr, &opts.LookupOptions)
if err != nil {
resp.AddError(addr, err)
return
}

resp.AddResult(result)
}(a)
}

// Don't need to check ctx here, because we pass through the ctx to all goroutines
// and the ctx is cancelled when the request is cancelled.
pool.Wait()
chix.JSON(w, r, http.StatusOK, resp)
}
42 changes: 37 additions & 5 deletions internal/handlers/apihandler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,59 @@
package apihandler

import (
_ "embed"
"net/http"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/lrstanley/chix"
"github.com/lrstanley/geoip/internal/httpware"
"github.com/lrstanley/geoip/internal/lookup"
)

type handler struct {
lookupSvc *lookup.Service
limiter *httpware.Limiter
}

func New(lookupSvc *lookup.Service) *handler {
func New(lookupSvc *lookup.Service, limiter *httpware.Limiter) *handler {
return &handler{
lookupSvc: lookupSvc,
limiter: limiter,
}
}

func (h *handler) Route(r chi.Router) {
r.Get("/{addr}", h.getLookup)
r.With(middleware.NoCache, middleware.GetHead).Get("/ping", func(w http.ResponseWriter, r *http.Request) {
chix.JSON(w, r, http.StatusOK, chix.M{"pong": true})
})
r.Use(
middleware.NoCache,
middleware.GetHead,
)

// v1 API.
r.With(h.limiter.Limit).Get("/{addr}", h.getLookupV1)
r.With(h.limiter.Skip, h.limiter.Limit).Get("/ping", h.ping)

// v2 API.
r.Get("/v2/openapi.yaml", h.getOpenAPI)
r.With(h.limiter.Limit).Get("/v2/lookup/{addr}", h.getLookupV2)
r.With(h.limiter.Limit).Post("/v2/bulk", h.postBulkV2)
r.With(h.limiter.Limit).Get("/v2/metadata", h.getMetadataV2)
r.With(h.limiter.Skip, h.limiter.Limit).Get("/v2/ping", h.ping)
r.NotFound(h.notFound)
}

func (h *handler) notFound(w http.ResponseWriter, r *http.Request) {
chix.Error(w, r, chix.WrapCode(http.StatusNotFound))
}

func (h *handler) ping(w http.ResponseWriter, r *http.Request) {
chix.JSON(w, r, http.StatusOK, chix.M{"pong": true})
}

//go:embed openapi_v2.yaml
var openapiv2 string

func (h *handler) getOpenAPI(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/yaml")
w.Write([]byte(openapiv2))
}
Loading

0 comments on commit d86a79a

Please sign in to comment.