Skip to content

Commit

Permalink
Refactor init and add compression support.
Browse files Browse the repository at this point in the history
- Break to `/v2` to introduce `Options{}` that's pass to lib init.
- Add `CompressOptions{}` to enable zstd|br|gzip|deflate auto-compression
  support offered by fasthttp.
  • Loading branch information
knadh committed Jan 31, 2025
1 parent 9a0624f commit 3595bd6
Show file tree
Hide file tree
Showing 16 changed files with 186 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
test:
strategy:
matrix:
go: ["1.21", "1.20", "1.18", "1.19"]
go: ["1.21"]

runs-on: ubuntu-20.04

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ write HTTP servers. It enables:
## Install

```bash
go get -u github.com/zerodha/fastglue
go get -u github.com/zerodha/fastglue/v2
```

## Usage

```go
import "github.com/zerodha/fastglue"
import "github.com/zerodha/fastglue/v2"
```

## Examples
Expand Down
4 changes: 2 additions & 2 deletions custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ type Envelope struct {

// NewGlue creates and returns a new instance of Fastglue with custom error
// handlers pre-bound.
func NewGlue() *Fastglue {
f := New()
func NewGlue(o Options) *Fastglue {
f := New(o)
f.Router.MethodNotAllowed = BadMethodHandler
f.Router.NotFound = NotFoundHandler
f.Router.SaveMatchedRoutePath = true
Expand Down
4 changes: 2 additions & 2 deletions examples/before-after/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"time"

"github.com/valyala/fasthttp"
"github.com/zerodha/fastglue"
"github.com/zerodha/fastglue/v2"
)

var (
Expand All @@ -18,7 +18,7 @@ var (
func main() {
flag.Parse()

g := fastglue.New()
g := fastglue.New(fastglue.Options{})
g.Before(setTime)
g.After(calculateTime)
g.GET("/", handleIndex)
Expand Down
4 changes: 2 additions & 2 deletions examples/decode/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"time"

"github.com/valyala/fasthttp"
"github.com/zerodha/fastglue"
"github.com/zerodha/fastglue/v2"
)

var (
Expand All @@ -17,7 +17,7 @@ var (
func main() {
flag.Parse()

g := fastglue.New()
g := fastglue.New(fastglue.Options{})
g.GET("/", handleIndex)

s := &fasthttp.Server{
Expand Down
2 changes: 1 addition & 1 deletion examples/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"time"

"github.com/valyala/fasthttp"
"github.com/zerodha/fastglue"
"github.com/zerodha/fastglue/v2"
)

// App is the global config "context" that'll be injected into every Request.
Expand Down
4 changes: 2 additions & 2 deletions examples/graceful/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"time"

"github.com/valyala/fasthttp"
"github.com/zerodha/fastglue"
"github.com/zerodha/fastglue/v2"
)

var (
Expand All @@ -19,7 +19,7 @@ var (
func main() {
flag.Parse()

g := fastglue.New()
g := fastglue.New(fastglue.Options{})
g.ServeStatic("/{filepath:*}", ".", true)

s := &fasthttp.Server{
Expand Down
4 changes: 2 additions & 2 deletions examples/helloworld/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"time"

"github.com/valyala/fasthttp"
"github.com/zerodha/fastglue"
"github.com/zerodha/fastglue/v2"
)

var (
Expand All @@ -17,7 +17,7 @@ var (
func main() {
flag.Parse()

g := fastglue.New()
g := fastglue.New(fastglue.Options{})
g.GET("/", handleHelloWorld)

s := &fasthttp.Server{
Expand Down
4 changes: 2 additions & 2 deletions examples/middleware/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"time"

"github.com/valyala/fasthttp"
"github.com/zerodha/fastglue"
"github.com/zerodha/fastglue/v2"
)

var (
Expand All @@ -19,7 +19,7 @@ var (
func main() {
flag.Parse()

g := fastglue.New()
g := fastglue.New(fastglue.Options{})
g.GET("/", auth(validateAll(handleGetAll)))
g.PUT("/", auth(fastglue.ReqLenParams(validate(handleMiddleware), map[string]int{"a": 5, "b": 5})))
g.POST("/", auth(fastglue.ReqParams(validate(handleMiddleware), []string{"a", "b"})))
Expand Down
4 changes: 2 additions & 2 deletions examples/path/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"time"

"github.com/valyala/fasthttp"
"github.com/zerodha/fastglue"
"github.com/zerodha/fastglue/v2"
)

var (
Expand All @@ -18,7 +18,7 @@ var (
func main() {
flag.Parse()

g := fastglue.New()
g := fastglue.New(fastglue.Options{})
g.GET("/", handleIndex)
g.GET("/{name:^[a-zA-Z]+$}", handleIndex)

Expand Down
4 changes: 2 additions & 2 deletions examples/singleton/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"time"

"github.com/valyala/fasthttp"
"github.com/zerodha/fastglue"
"github.com/zerodha/fastglue/v2"
)

var (
Expand All @@ -29,7 +29,7 @@ func main() {
log: log.New(os.Stdout, "SINGLETON", log.Llongfile),
}

g := fastglue.New()
g := fastglue.New(fastglue.Options{})
g.SetContext(app)
g.GET("/", handleIndex)

Expand Down
4 changes: 2 additions & 2 deletions examples/static-file/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"time"

"github.com/valyala/fasthttp"
"github.com/zerodha/fastglue"
"github.com/zerodha/fastglue/v2"
)

var (
Expand All @@ -16,7 +16,7 @@ var (
func main() {
flag.Parse()

g := fastglue.New()
g := fastglue.New(fastglue.Options{})
g.ServeStatic("/{filepath:*}", ".", true)

s := &fasthttp.Server{
Expand Down
82 changes: 76 additions & 6 deletions fastglue.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ var (
authToken = []byte("token")
)

// CompressionType is the type of otuput compression available in fasthttp
// for the response body.
type CompressionType string

const (
CompressionZstd CompressionType = "zstd"
CompressionBr CompressionType = "br"
CompressionGzip CompressionType = "gzip"
CompressionDeflate CompressionType = "deflate"
)

// FastRequestHandler is the fastglue HTTP request handler function
// that wraps over the fasthttp handler.
type FastRequestHandler func(*Request) error
Expand All @@ -53,21 +64,52 @@ type Request struct {
Context interface{}
}

// CompressionOpt is the configuration for enabling and controlling compression.
// Enabling this will automatically compress the response based on the client's
// Accept-Encoding header by internally invoking fasthttp.CompressHandler()
// gzip|deflate|br|zstd are the supported compression types.
type CompressionOpt struct {
Enabled bool

// Type of compression to support (std, gzip, deflate, br). If no type is specified
// then fasthttp's default compression types and its internal order of priority are used.
//
// Important: The first type in the list is the preferred type
// irrespective of the ordering of types in the incoming client's Accept-Encoding header.
// That is because fasthttp's CompressHandler() internally uses arbitrary
// type ordering to compress the response. fastglue thus overrides the
// Accept-Encoding header to only have the first type in this list.
// For instance, if the list here is [zstd, br], and the incoming header is
// [gzip, deflate, br, zstd], fastglue will overwrite the header to [zstd],
// forcing fasthttp to compress the response using the preferred type here.
Types []CompressionType
}

// Fastglue is the "glue" wrapper over fasthttp and fasthttprouter.
type Fastglue struct {
Router *fasthttprouter.Router
Server *fasthttp.Server
context interface{}
MatchedRoutePathParam string
before []FastMiddleware
after []FastMiddleware

context interface{}
before []FastMiddleware
after []FastMiddleware

opt Options
}

type Options struct {
CompressionOpt CompressionOpt
}

// New creates and returns a new instance of Fastglue.
func New() *Fastglue {
return &Fastglue{
func New(o Options) *Fastglue {
f := &Fastglue{
Router: fasthttprouter.New(),
opt: o,
}

return f
}

// ListenAndServe is a wrapper for fasthttp.ListenAndServe. It takes a TCP address,
Expand Down Expand Up @@ -150,7 +192,7 @@ func (f *Fastglue) Shutdown(s *fasthttp.Server, shutdownComplete chan error) {
// handler is the "proxy" abstraction that converts a fastglue handler into
// a fasthttp handler and passes execution in and out.
func (f *Fastglue) handler(h FastRequestHandler) func(*fasthttp.RequestCtx) {
return func(ctx *fasthttp.RequestCtx) {
handler := func(ctx *fasthttp.RequestCtx) {
req := &Request{
RequestCtx: ctx,
Context: f.context,
Expand All @@ -172,7 +214,35 @@ func (f *Fastglue) handler(h FastRequestHandler) func(*fasthttp.RequestCtx) {
}
}

// If compression is enabled, override the response header to
// the preferred type in the config.
if f.opt.CompressionOpt.Enabled {
for _, typ := range f.opt.CompressionOpt.Types {
t := string(typ)
// If the preferred type is in the client's Accept-Encoding header,
// overwrite the request header to only have this type, forcing
// fasthttp.CompressHandler() to compress the response using it.
// This is because fasthttp internally does not respect the order
// in the client header and uses arbitrary ordering to compress the response.
if ctx.Request.Header.HasAcceptEncoding(t) {
ctx.Request.Header.Set("Accept-Encoding", t)
break
}
}
}
}

// If compression is enabled, wrap the handler with fasthttp's CompressHandler
// which automatically handles the compression logic.
if f.opt.CompressionOpt.Enabled {
// fasthttp's compression handlers are pretty bad. This particular handler
// is the one that supports gzip|br|zstd|deflate.
return fasthttp.CompressHandlerBrotliLevel(handler,
fasthttp.CompressBrotliDefaultCompression,
fasthttp.CompressDefaultCompression)
}

return handler
}

// Handler returns fastglue's central fasthttp handler that can be registered
Expand Down
Loading

0 comments on commit 3595bd6

Please sign in to comment.