Skip to content

Commit

Permalink
Feature/rate limit (#35)
Browse files Browse the repository at this point in the history
* Added reate limiter

* Removed redundant file

* Updated README
  • Loading branch information
ziflex authored Mar 29, 2023
1 parent 96acba6 commit e9bdbcb
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 44 deletions.
35 changes: 20 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,24 @@ Health check endpoint (for Kubernetes, e.g.). Returns empty 200.
### Run commands

```bash
-cache-size uint
amount of cached queries. 0 means no caching (default 100)
-chrome-ip string
Google Chrome remote IP address (default "127.0.0.1")
-chrome-port uint
Google Chrome remote debugging port (default 9222)
-log-level string
log level (default "debug")
-port uint
port to listen (default 8080)
-version
show version
-help
show this list

-log-level="debug"
log level
-port=8080
port to listen
-body-limit=1000
maximum size of request body in kb. 0 means no limit.
-request-limit=20
amount of requests per second for each IP. 0 means no limit.
-cache-size=100
amount of cached queries. 0 means no caching.
-chrome-ip="127.0.0.1"
Google Chrome remote IP address
-chrome-port=9222
Google Chrome remote debugging port
-no-chrome=false
disable Chrome driver
-version=false
show version
-help=false
show this list
```
4 changes: 4 additions & 0 deletions internal/controllers/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ func (c *Health) Use(e *echo.Echo) {
}

func (c *Health) healthCheck(ctx echo.Context) error {
if c.settings.Disabled {
return ctx.NoContent(http.StatusOK)
}

out, err := http.Get(c.settings.VersionURL())

if err != nil {
Expand Down
34 changes: 20 additions & 14 deletions internal/controllers/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,32 +87,38 @@ func (c *Info) Use(e *echo.Echo) {
}

func (c *Info) version(_ context.Context) (VersionDto, error) {
chromeVersionResp, err := http.Get(c.settings.CDP.VersionURL())
var chromeVersion ChromeVersionDto

if err != nil {
return VersionDto{}, errors.Wrap(err, "call Chrome")
}
if !c.settings.CDP.Disabled {
chromeVersionResp, err := http.Get(c.settings.CDP.VersionURL())

defer chromeVersionResp.Body.Close()
if err != nil {
return VersionDto{}, errors.Wrap(err, "call Chrome")
}

chromeVersionBlob, err := io.ReadAll(chromeVersionResp.Body)
defer chromeVersionResp.Body.Close()

if err != nil {
return VersionDto{}, errors.Wrap(err, "read response from Chrome")
}
chromeVersionBlob, err := io.ReadAll(chromeVersionResp.Body)

chromeVersion := chromeVersionInternal{}
if err != nil {
return VersionDto{}, errors.Wrap(err, "read response from Chrome")
}

err = json.Unmarshal(chromeVersionBlob, &chromeVersion)
chromeVersionInternal := chromeVersionInternal{}

if err != nil {
return VersionDto{}, errors.Wrap(err, "parse response from Chrome")
err = json.Unmarshal(chromeVersionBlob, &chromeVersionInternal)

if err != nil {
return VersionDto{}, errors.Wrap(err, "parse response from Chrome")
}

chromeVersion = ChromeVersionDto(chromeVersionInternal)
}

return VersionDto{
Worker: c.settings.Version,
Ferret: c.settings.FerretVersion,
Chrome: ChromeVersionDto(chromeVersion),
Chrome: chromeVersion,
}, nil
}

Expand Down
35 changes: 29 additions & 6 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,51 @@ import (
"fmt"
"net/http"

"golang.org/x/time/rate"

"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/ziflex/lecho/v3"
)

// Server is HTTP server that wraps Ferret worker.
type Server struct {
router *echo.Echo
}
type (
// Options is a set of options for server.
Options struct {
// RequestLimit is a number of requests per second for each IP.
// If value is 0, rate limit is disabled.
RequestLimit uint64

// BodyLimit is a maximum size of request body.
// If value is 0, body limit is disabled.
BodyLimit uint64
}

// Server is HTTP server that wraps Ferret worker.
Server struct {
router *echo.Echo
}
)

func New(logger *lecho.Logger) (*Server, error) {
func New(logger *lecho.Logger, opts Options) (*Server, error) {
router := echo.New()
router.Logger = logger
router.HideBanner = true

if opts.RequestLimit > 0 {
router.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(rate.Limit(opts.RequestLimit))))
}

router.Use(middleware.CORSWithConfig(middleware.CORSConfig{
Skipper: middleware.DefaultSkipper,
AllowOrigins: []string{"*"},
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
}))
router.Use(middleware.BodyLimit("1M"))

if opts.BodyLimit > 0 {
router.Use(middleware.BodyLimit(fmt.Sprintf("%d", opts.BodyLimit)))
}

router.Use(middleware.RequestID())
router.Use(lecho.Middleware(lecho.Config{
Logger: logger,
Expand Down
40 changes: 33 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ var (

port = flag.Uint64("port", 8080, "port to listen")

noChrome = flag.Bool("no-chrome", false, "disable Chrome driver")

chromeIP = flag.String("chrome-ip", "127.0.0.1", "Google Chrome remote IP address")

chromeDebuggingPort = flag.Uint64("chrome-port", 9222, "Google Chrome remote debugging port")
Expand All @@ -42,6 +44,18 @@ var (
"amount of cached queries. 0 means no caching.",
)

requestLimit = flag.Uint64(
"request-limit",
20,
"amount of requests per second for each IP. 0 means no limit.",
)

bodyLimit = flag.Uint64(
"body-limit",
1000,
"maximum size of request body in kb. 0 means no limit.",
)

showVersion = flag.Bool(
"version",
false,
Expand Down Expand Up @@ -84,15 +98,19 @@ func main() {
)

cdp := worker.CDPSettings{
Host: *chromeIP,
Port: *chromeDebuggingPort,
Host: *chromeIP,
Port: *chromeDebuggingPort,
Disabled: *noChrome,
}

if err := waitForChrome(cdp); err != nil {
logger.Fatalf("wait for Chrome: %s", err)
}

srv, err := server.New(logger)
srv, err := server.New(logger, server.Options{
RequestLimit: *requestLimit,
BodyLimit: *bodyLimit,
})

if err != nil {
logger.Fatal(err)
Expand All @@ -104,10 +122,14 @@ func main() {
logger.Fatal(errors.Wrap(err, "create cache storage"))
}

wkr, err := worker.New(
worker.WithCustomCDP(cdp),
worker.WithCache(cache),
)
opts := make([]worker.Option, 0, 2)
opts = append(opts, worker.WithCache(cache))

if !cdp.Disabled {
opts = append(opts, worker.WithCustomCDP(cdp))
}

wkr, err := worker.New(opts...)

if err != nil {
logger.Fatal(errors.Wrap(err, "create a worker instance"))
Expand All @@ -123,6 +145,10 @@ func main() {
}

func waitForChrome(cdp worker.CDPSettings) error {
if cdp.Disabled {
return nil
}

runner := waitfor.New(http.Use())

return runner.Test(context.Background(), []string{
Expand Down
5 changes: 3 additions & 2 deletions pkg/worker/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import (

type (
CDPSettings struct {
Host string `json:"host"`
Port uint64 `json:"port"`
Host string `json:"host"`
Port uint64 `json:"port"`
Disabled bool `json:"disabled"`
}

Options struct {
Expand Down

0 comments on commit e9bdbcb

Please sign in to comment.