Browse Source

Merge branch 'main' into feature/add-http-image-upload

feature/add-http-image-upload
Roman Zipp 2 years ago
parent
commit
f5c5d2ca86
  1. 54
      README.md
  2. 3
      build.sh
  3. 3
      go.mod
  4. 21
      go.sum
  5. 34
      main.go
  6. 2
      middlewares/authorization.go
  7. 2
      middlewares/authorization_test.go
  8. 43
      middlewares/ratelimiter.go
  9. 64
      middlewares/ratelimiter_test.go
  10. 28
      settings/settings.go
  11. 12
      settings/settings_test.go

54
README.md

@ -10,7 +10,7 @@ Micro-service for file storage and processing written in Go.
## Requirements
- [Go 1.17+](https://go.dev/)
- [*Docker*](https://docs.docker.com/) (optional)
- [_Docker_](https://docs.docker.com/) (optional)
## Setup
@ -56,13 +56,43 @@ go test ./... -v
docker run --rm -v "$PWD":/usr/src/lithium -w /usr/src/lithium golang:1.17 go test ./...
```
## Configuration
Config options can be adjusted via the [`settings.json`](settings.json) file in the root directory.
```json
{
"endpoint": "0.0.0.0:8000",
"token": "changeme",
"rate_limiter": {
"requests_per_minute": 20,
"allowed_burst": 5
},
"storage_provider": {
"type": 0,
"base_path": "assets"
}
}
```
## Rate Limiting
By default, the rate limiting takes place on a per-route basis. When the limit for a specific route is hit, the response will return a status code `429: Too Many Requests`.
| Headers | Explanation |
| --------------------- | -------------------------------- |
| X-Ratelimit-Limit | Allowed requests per minute |
| X-Ratelimit-Remaining | Remaining requests |
| X-Ratelimit-Reset | Seconds until requests replenish |
## API
### `GET` `/`
Show application information.
Show application information.
**Required headers**:
```shell
Authorization: Bearer <Token>
```
@ -81,6 +111,7 @@ Authorization: Bearer <Token>
Show pipeline information.
**Required headers**:
```shell
Authorization: Bearer <Token>
```
@ -111,27 +142,12 @@ Authorization: Bearer <Token>
}
```
## Configuration
Config options can be adjusted via the [`settings.json`](settings.json) file in the root directory.
```json
{
"endpoint": "0.0.0.0:8000",
"token": "changeme",
"storage_provider": {
"type": 0,
"base_path": "assets"
}
}
```
## Pipelines
The project uses a pipeline system defined by [JSON](https://en.wikipedia.org/wiki/JSON) files located in the [config](config) folder.
Take a look at the [example pipeline configuration file](config/example.json).
Available pipeline `type`s: `0` (Image), `1` (Video)
Available pipeline `type`s: `0` (Image), `1` (Video)
```json
{
@ -148,7 +164,7 @@ Available pipeline `type`s: `0` (Image), `1` (Video)
The image pipeline offers the following additional output options.
Available `format` types: `jpg` (or `jpeg`), `png`, `gif`, `tif` (or `tiff`) and `bmp`
Available `format` types: `jpg` (or `jpeg`), `png`, `gif`, `tif` (or `tiff`) and `bmp`
The `quality` field can contain any integer between 1 and 100.
```json

3
build.sh

@ -0,0 +1,3 @@
#!/bin/bash
GIT_COMMIT=$(git rev-parse --short HEAD); go build -ldflags "-X main.GitCommit=$GIT_COMMIT"

3
go.mod

@ -8,10 +8,13 @@ require (
github.com/gorilla/mux v1.8.0
github.com/spf13/afero v1.8.0
github.com/stretchr/testify v1.7.0
github.com/throttled/throttled v2.2.5+incompatible
github.com/throttled/throttled/v2 v2.9.0
)
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 // indirect

21
go.sum

@ -59,9 +59,11 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -87,6 +89,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -120,6 +123,9 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@ -133,6 +139,9 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
@ -150,6 +159,10 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ=
github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos=
github.com/throttled/throttled/v2 v2.9.0 h1:DOkCb1el7NYzRoPb1pyeHVghsUoonVWEjmo34vrcp/8=
github.com/throttled/throttled/v2 v2.9.0/go.mod h1:0JHxhGAidPyqbgD4HF8Y1sNFfG0ffVXK6C8EpkNdLEM=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -205,6 +218,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -215,6 +229,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -254,6 +269,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/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=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -263,6 +279,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -443,7 +460,11 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

34
main.go

@ -3,22 +3,26 @@ package main
import (
"bytes"
"encoding/json"
"github.com/geplauder/lithium/auth"
"io"
"net/http"
"github.com/geplauder/lithium/middlewares"
"github.com/geplauder/lithium/pipelines"
"github.com/geplauder/lithium/settings"
"github.com/geplauder/lithium/storage"
"github.com/gorilla/mux"
"github.com/spf13/afero"
"io"
"net/http"
)
const Name string = "Lithium"
const Version string = "0.1.0"
var GitCommit string
type Metadata struct {
Name string `json:"name"`
Version string `json:"version"`
Name string `json:"name"`
Version string `json:"version"`
CommitHash string `json:"commit_hash"`
}
func PipelineHandler(pipeline pipelines.IPipeline, storageProvider storage.IStorageProvider, w http.ResponseWriter, r *http.Request) {
@ -31,7 +35,7 @@ func PipelineHandler(pipeline pipelines.IPipeline, storageProvider storage.IStor
func IndexHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(Metadata{Name, Version})
err := json.NewEncoder(w).Encode(Metadata{Name, Version, GitCommit})
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
@ -155,10 +159,22 @@ func main() {
pipes := pipelines.LoadPipelines()
authMiddleware := auth.CreateAuthenticationMiddleware(appSettings.Token)
r := mux.NewRouter()
r.Use(authMiddleware.Middleware)
if appSettings.Authentication.Enabled {
authMiddleware := middlewares.CreateAuthenticationMiddleware(appSettings.Authentication.Token)
r.Use(authMiddleware.Middleware)
}
if appSettings.RateLimiter.Enabled {
rateLimiterMiddleware, err := middlewares.CreateRateLimiterMiddleware(appSettings.RateLimiter.RequestsPerMinute, appSettings.RateLimiter.AllowedBurst)
if err != nil {
panic(err)
}
r.Use(rateLimiterMiddleware.Middleware)
}
RegisterRoutes(r, pipes, storageProvider)

2
auth/authorization.go → middlewares/authorization.go

@ -1,4 +1,4 @@
package auth
package middlewares
import (
"net/http"

2
auth/authorization_test.go → middlewares/authorization_test.go

@ -1,4 +1,4 @@
package auth
package middlewares
import (
"net/http"

43
middlewares/ratelimiter.go

@ -0,0 +1,43 @@
package middlewares
import (
"net/http"
"github.com/throttled/throttled/store/memstore"
"github.com/throttled/throttled/v2"
)
type RateLimiterMiddleware struct {
rateLimiter throttled.HTTPRateLimiter
}
func (middleware RateLimiterMiddleware) Middleware(next http.Handler) http.Handler {
return middleware.rateLimiter.RateLimit(next)
}
func CreateRateLimiterMiddleware(requestsPerMinute int, allowedBurst int) (*RateLimiterMiddleware, error) {
store, err := memstore.New(65536)
if err != nil {
return nil, err
}
quota := throttled.RateQuota{
MaxRate: throttled.PerMin(requestsPerMinute),
MaxBurst: allowedBurst,
}
rateLimiter, err := throttled.NewGCRARateLimiter(store, quota)
if err != nil {
return nil, err
}
httpRateLimiter := throttled.HTTPRateLimiter{
RateLimiter: rateLimiter,
VaryBy: &throttled.VaryBy{Path: true},
}
return &RateLimiterMiddleware{
rateLimiter: httpRateLimiter,
}, nil
}

64
middlewares/ratelimiter_test.go

@ -0,0 +1,64 @@
package middlewares
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func ExecuteRequest(middlewareHandler http.Handler) int {
request, _ := http.NewRequest("GET", "/", nil)
responseRecorder := httptest.NewRecorder()
middlewareHandler.ServeHTTP(responseRecorder, request)
return responseRecorder.Code
}
func TestRateLimiterMiddleware(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
t.Run("AuthorizationMiddleware returns 200 response when rate limit is not hit", func(t *testing.T) {
middleware, err := CreateRateLimiterMiddleware(1, 0)
assert.Nil(t, err)
middlewareHandler := middleware.Middleware(handler)
assert.Equal(t, 200, ExecuteRequest(middlewareHandler))
})
t.Run("AuthorizationMiddleware returns 429 response when rate limit is hit", func(t *testing.T) {
middleware, err := CreateRateLimiterMiddleware(1, 0)
assert.Nil(t, err)
middlewareHandler := middleware.Middleware(handler)
assert.Equal(t, 200, ExecuteRequest(middlewareHandler))
assert.Equal(t, 429, ExecuteRequest(middlewareHandler))
})
t.Run("AuthorizationMiddleware returns 200 response when rate limit with burst is not hit", func(t *testing.T) {
middleware, err := CreateRateLimiterMiddleware(1, 1)
assert.Nil(t, err)
middlewareHandler := middleware.Middleware(handler)
assert.Equal(t, 200, ExecuteRequest(middlewareHandler))
assert.Equal(t, 200, ExecuteRequest(middlewareHandler))
})
t.Run("AuthorizationMiddleware returns 429 response when rate limit with burst is hit", func(t *testing.T) {
middleware, err := CreateRateLimiterMiddleware(1, 1)
assert.Nil(t, err)
middlewareHandler := middleware.Middleware(handler)
assert.Equal(t, 200, ExecuteRequest(middlewareHandler))
assert.Equal(t, 200, ExecuteRequest(middlewareHandler))
assert.Equal(t, 429, ExecuteRequest(middlewareHandler))
})
}

28
settings/settings.go

@ -15,9 +15,10 @@ const (
type FileSystemType int
type Settings struct {
Endpoint string `json:"endpoint"`
Token string `json:"token"`
StorageProvider StorageSettings `json:"storage_provider"`
Endpoint string `json:"endpoint"`
Authentication AuthenticationSettings `json:"authentication"`
RateLimiter RateLimiterSettings `json:"rate_limiter"`
StorageProvider StorageSettings `json:"storage_provider"`
}
type StorageSettings struct {
@ -25,6 +26,17 @@ type StorageSettings struct {
BasePath string `json:"base_path"`
}
type AuthenticationSettings struct {
Enabled bool `json:"enabled"`
Token string `json:"token"`
}
type RateLimiterSettings struct {
Enabled bool `json:"enabled"`
RequestsPerMinute int `json:"requests_per_minute"`
AllowedBurst int `json:"allowed_burst"`
}
func parseSettings(data []byte) Settings {
settings := Settings{}
@ -50,7 +62,15 @@ func LoadSettings(fileSystem afero.Fs) (Settings, error) {
// If file does not exist, create default settings
defaultSettings := Settings{
Endpoint: "127.0.0.1:8000",
Token: "changeme",
Authentication: AuthenticationSettings{
Enabled: true,
Token: "changeme",
},
RateLimiter: RateLimiterSettings{
Enabled: true,
RequestsPerMinute: 20,
AllowedBurst: 5,
},
StorageProvider: StorageSettings{
Type: Local,
BasePath: "assets",

12
settings/settings_test.go

@ -12,7 +12,15 @@ import (
func TestSettingsParsing(t *testing.T) {
const file string = `{
"endpoint": "0.0.0.0:8000",
"token": "foobar",
"authentication": {
"enabled": true,
"token": "foobar"
},
"rate_limiter": {
"enabled": true,
"requests_per_minute": 20,
"allowed_burst": 5
},
"storage_provider": {
"type": 0,
"base_path": "assets"
@ -23,7 +31,7 @@ func TestSettingsParsing(t *testing.T) {
settings := parseSettings([]byte(file))
assert.Equal(t, "0.0.0.0:8000", settings.Endpoint)
assert.Equal(t, "foobar", settings.Token)
assert.Equal(t, "foobar", settings.Authentication.Token)
assert.Equal(t, "assets", settings.StorageProvider.BasePath)
})
}

Loading…
Cancel
Save