Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
6a7e9a3
fix breaking out of iterator
flrdv Aug 12, 2025
79f1cfa
merged some of files separately containing methods for router togethe…
flrdv Aug 13, 2025
1fbb728
minor update (removed TODO)
flrdv Aug 13, 2025
fe7d972
made in path normalization deleting just single trailing slash
flrdv Aug 13, 2025
8f8790f
fixed not appending HEAD to Allow on resources where HEAD is implicit…
flrdv Aug 13, 2025
a8484cb
added server-wide options and fixed automatic OPTIONS on specific end…
flrdv Aug 13, 2025
ee8677b
added HTTP mime type
flrdv Aug 13, 2025
33d910a
refactored TRACE capabilities. Disabled it by default. Using internal…
flrdv Aug 13, 2025
19f04d7
squashed together SizedStream and Stream into a single method. Added …
flrdv Aug 13, 2025
0c49817
heavily optimized the prefix tree for dynamic routing by using binary…
flrdv Aug 13, 2025
2a90968
refactored handling default headers
flrdv Aug 18, 2025
c4aa31e
minor internal updates
flrdv Aug 18, 2025
c8996d8
added buffering for chunked transfer encoding (but still missing it f…
flrdv Aug 18, 2025
d57350c
removed unnecessary flushes for sized streams when going unbuffered
flrdv Aug 21, 2025
d86f407
minor update
flrdv Aug 21, 2025
19cf6d1
added gzip bench case
flrdv Aug 21, 2025
7d4a24b
enable body buffering by default
flrdv Aug 21, 2025
661f485
minor internal updates
flrdv Aug 24, 2025
c833d6c
moved out codecs boilerplate into a base entity
flrdv Aug 24, 2025
7b28e2a
added deflate and zstd codecs
flrdv Aug 24, 2025
4e76a1a
added buffering sized streams, fixed reading over the stream size
flrdv Aug 24, 2025
018ca3a
updated documentation
flrdv Aug 25, 2025
b3324b1
removed request.Encoding struct, moved all its attributes into the re…
flrdv Aug 26, 2025
a2f2228
now appending Content-Length to headers just like all others
flrdv Aug 26, 2025
d13acec
allocating buffers for codecs lazily, added passing the size automati…
flrdv Aug 26, 2025
70291f1
added auto compressing based on client preferences, made small bodies…
flrdv Aug 26, 2025
1f8b3b1
inserting new entries instead of deleted ones
flrdv Aug 27, 2025
8941bc2
added the codec suit, a collection of all available codecs
flrdv Aug 28, 2025
9bfef1c
updated docs & minor code improvements
flrdv Aug 28, 2025
e45209a
added transparent echo to demo
flrdv Aug 28, 2025
1cd9803
added Autocompress middleware to compress response bodies automatically
flrdv Aug 28, 2025
c645ddd
added ignoring Transfer-Encoding: identity header, now it's considere…
flrdv Aug 28, 2025
f71d792
added unsized mode to identityWriter to support HTTP/1.0, which doesn…
flrdv Aug 28, 2025
7cb5332
implemented Len() interface for body to support sized echoing if the …
flrdv Aug 28, 2025
f5cb29a
updated docs
flrdv Aug 28, 2025
507b158
avoid urldecoding of unsafe chars (slashes): now slashes can be regis…
flrdv Aug 28, 2025
b7be6de
added escaping colon to avoid confusions between wildcards and and no…
flrdv Aug 28, 2025
fe334a5
got rid of outdated TODOs
flrdv Aug 29, 2025
a3edce6
eliminated an allocation on each connection by generating Accept-Enco…
flrdv Aug 29, 2025
421b0f5
fixed panic when responding with status code <100
flrdv Aug 29, 2025
59fc57f
added tests for escaping paths
flrdv Aug 29, 2025
4b6ff4f
not automatically decoding chars that are considered unsafe
flrdv Aug 29, 2025
a458752
added decoding and normalizing urlencoded chars when registering endp…
flrdv Aug 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ type (
// any request with body (each call to request's body will result in status.ErrBodyTooLarge).
// In order to disable the setting, use the math.MaxUInt64 value.
MaxSize uint64
//// DecodingBufferSize is a size of a buffer, used to store decoded request's body
//DecodingBufferSize int
// Form is either application/x-www-form-urlencoded or multipart/form-data. Due to their common
// nature, they are easy to be generalized.
Form BodyForm
}

Expand All @@ -96,6 +96,10 @@ type (
// 2) If a stream is unsized (1) and the previous write used more than ~98.44% of its
// capacity (2), the capacity doubles.
WriteBufferSize NETWriteBufferSize
// SmallBody limits how big must a response body be in order to be compressed, if the
// auto compression option is enabled. This setting doesn't affect enforced compression
// options and unsized streams.
SmallBody int64
}
)

Expand Down Expand Up @@ -158,6 +162,7 @@ func Default() *Config {
Default: 2 * 1024,
Maximal: 64 * 1024,
},
SmallBody: 4 * 1024,
},
}
}
2 changes: 1 addition & 1 deletion examples/compression/compression.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func Shout(r *http.Request) *http.Response {

func main() {
app := indigo.New(":8080").
Codec(codec.NewGZIP()).
Codec(codec.Suit()...).
OnBind(func(addr string) {
fmt.Println("Listening on", addr)
})
Expand Down
8 changes: 6 additions & 2 deletions examples/demo/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func main() {
app := indigo.New(":8080").
TLS(":8443", indigo.LocalCert()).
Tune(s).
Codec(codec.NewGZIP()).
Codec(codec.Suit()...).
OnBind(func(addr string) {
log.Printf("running on %s\n", addr)
})
Expand All @@ -71,10 +71,14 @@ func main() {
Use(middleware.LogRequests()).
Alias("/", "/static/index.html", method.GET).
Alias("/favicon.ico", "/static/favicon.ico", method.GET).
Static("/static", "examples/demo/static")
Static("/static", "examples/demo/static", middleware.Autocompress)

r.Get("/stress", Stressful, middleware.Recover)

r.Post("/echo", func(request *http.Request) *http.Response {
return http.Stream(request, request.Body)
})

r.Resource("/").
Post(IndexSay)

Expand Down
12 changes: 9 additions & 3 deletions http/body.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ type Body struct {
form form.Form
}

// TODO: body entity can be passed by value (?)

func NewBody(src Fetcher) *Body {
return &Body{
Fetcher: src,
Expand Down Expand Up @@ -79,7 +77,7 @@ func (b *Body) Bytes() ([]byte, error) {
}

newSize := int(b.request.cfg.Body.Form.BufferPrealloc)
if !b.request.Encoding.Chunked {
if !b.request.Chunked {
newSize = min(b.request.ContentLength, int(b.request.cfg.Body.MaxSize))
}

Expand Down Expand Up @@ -178,6 +176,14 @@ func (b *Body) Form() (f form.Form, err error) {
}
}

func (b *Body) Len() int {
if b.request.Chunked {
return -1
}

return b.request.ContentLength
}

// Discard sinkholes the rest of the body. Should not be used unless you know what you're doing.
func (b *Body) Discard() error {
for b.error == nil {
Expand Down
37 changes: 0 additions & 37 deletions http/codec/adapter.go

This file was deleted.

146 changes: 146 additions & 0 deletions http/codec/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package codec

import (
"io"

"github.com/indigo-web/indigo/http"
)

var _ Codec = baseCodec{}

type instantiator = func() Instance

type baseCodec struct {
token string
newInst instantiator
}

func newBaseCodec(token string, newInst instantiator) baseCodec {
return baseCodec{
token: token,
newInst: newInst,
}
}

func (b baseCodec) Token() string {
return b.token
}

func (b baseCodec) New() Instance {
return b.newInst()
}

var _ Instance = new(baseInstance)

type (
decoderResetter = func(io.Reader, *readerAdapter) error

writeResetter interface {
io.WriteCloser
Reset(dst io.Writer)
}
)

type baseInstance struct {
reset decoderResetter
adapter *readerAdapter
w writeResetter // compressor
r io.Reader // decompressor
dst io.Closer
buff []byte
}

func newBaseInstance(encoder writeResetter, decoder io.Reader, reset decoderResetter) instantiator {
return func() Instance {
return &baseInstance{
reset: reset,
adapter: newAdapter(),
w: encoder,
r: decoder,
}
}
}

func (b *baseInstance) ResetCompressor(w io.Writer) {
b.w.Reset(w)
b.dst = nil

if c, ok := w.(io.Closer); ok {
b.dst = c
}
}

func (b *baseInstance) Write(p []byte) (n int, err error) {
return b.w.Write(p)
}

func (b *baseInstance) Close() error {
if err := b.w.Close(); err != nil {
return err
}

if b.dst != nil {
return b.dst.Close()
}

return nil
}

func (b *baseInstance) ResetDecompressor(source http.Fetcher, bufferSize int) error {
if cap(b.buff) < bufferSize {
b.buff = make([]byte, bufferSize)
}

b.adapter.Reset(source)

return b.reset(b.r, b.adapter)
}

func (b *baseInstance) Fetch() ([]byte, error) {
n, err := b.r.Read(b.buff)
return b.buff[:n], err
}

func genericResetter(r io.Reader, adapter *readerAdapter) error {
type resetter interface {
Reset(r io.Reader) error
}

if reset, ok := r.(resetter); ok {
return reset.Reset(adapter)
}

return nil
}

type readerAdapter struct {
fetcher http.Fetcher
err error
data []byte
}

func newAdapter() *readerAdapter {
return new(readerAdapter)
}

func (r *readerAdapter) Read(b []byte) (n int, err error) {
if len(r.data) == 0 {
if r.err != nil {
return 0, r.err
}

r.data, r.err = r.fetcher.Fetch()
}

n = copy(b, r.data)
r.data = r.data[n:]
if len(r.data) == 0 {
err = r.err
}

return n, err
}

func (r *readerAdapter) Reset(fetcher http.Fetcher) {
*r = readerAdapter{fetcher: fetcher}
}
10 changes: 9 additions & 1 deletion http/codec/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ type Compressor interface {
}

type Decompressor interface {
ResetDecompressor(source http.Fetcher) error
http.Fetcher
ResetDecompressor(source http.Fetcher, bufferSize int) error
}

// Suit is a collection of out-of-the-box supported codecs. It contains:
// - gzip
// - deflate
// - zstd
func Suit() []Codec {
return []Codec{NewGZIP(), NewDeflate(), NewZSTD()}
}
20 changes: 20 additions & 0 deletions http/codec/deflate.go
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
package codec

import (
"io"

"github.com/klauspost/compress/flate"
)

func NewDeflate() Codec {
writer, err := flate.NewWriter(nil, 5)
if err != nil {
panic(err)
}

reader := flate.NewReader(nil)
instantiator := newBaseInstance(writer, reader, func(r io.Reader, a *readerAdapter) error {
return r.(flate.Resetter).Reset(a, nil)
})

return newBaseCodec("deflate", instantiator)
}
9 changes: 9 additions & 0 deletions http/codec/deflate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package codec

import (
"testing"
)

func TestFlate(t *testing.T) {
testCodec(t, NewDeflate().New())
}
Loading
Loading