Code Monkey home page Code Monkey logo

atreugo's Introduction

Atreugo

Test status Coverage Status Go Report Card GoDev GitHub release

High performance and extensible micro web framework with zero memory allocations in hot paths.

It's built on top of fasthttp.

Install

go get github.com/savsgio/atreugo/v11

Supported Go versions:

  • 1.22.x
  • 1.21.x
  • 1.20.x
  • 1.19.x

Documentation

See: docs

Organization

Find useful libraries like middlewares, websocket, etc.

Feature Overview

  • Optimized for speed. Easily handles more than 100K qps and more than 1M concurrent keep-alive connections on modern hardware.

  • Optimized for low memory usage.

  • Easy 'Connection: Upgrade' support via RequestCtx.Hijack.

  • Server provides anti-DoS limits.

  • Middlewares support:

    • Before view execution.
    • After view execution.
  • Easy routing:

    • Path parameters (mandatories and optionals).
    • Views with timeout.
    • Group paths and middlewares.
    • Static files.
    • Serve one file like pdf, etc.
    • Middlewares for specific views.
    • fasthttp handlers support.
    • net/http handlers support.
  • Common responses (also you could use your own responses):

    • JSON
    • HTTP
    • Text
    • Raw
    • File
    • Redirect

Examples:

Go to examples to see how to use Atreugo.

Note:

*atreugo.RequestCtx is equal to *fasthttp.RequestCtx, but with extra functionalities, so you can use the same functions of *fasthttp.RequestCtx. Don't worry 😄

Benchmark

Best Performance: Atreugo is one of the fastest go web frameworks in the go-web-framework-benchmark.

  • Basic Test: The first test case is to mock 0 ms, 10 ms, 100 ms, 500 ms processing time in handlers.

  • Concurrency Test (allocations): In 30 ms processing time, the test result for 100, 1000, 5000 clients is:

* Smaller is better

Contributing

Feel free to contribute or fork me... 😉

atreugo's People

Contributors

3job avatar bivas avatar cyannuk avatar dependabot[bot] avatar dmarkhas avatar philippgille avatar rantav avatar savsgio avatar shakedoron avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

atreugo's Issues

Cannot update/download deps

go: github.com/savsgio/atreugo/[email protected] requires
	github.com/fasthttp/[email protected]/go.mod: verifying module: checksum mismatch
	downloaded: h1:7KEYuV4ieG9kNJqqxnH0pwIdO69cJCVhVqZx3CpOURw=
	sum.golang.org: h1:GllqmaKtAsIvYwz5Nbu0qcbQQXBSVaeXw2KY3SmlbYM=

SECURITY ERROR
This download does NOT match the one reported by the checksum server.
The bits may have been replaced on the origin server, or an attacker may
have intercepted the download attempt.

For more information, see 'go help module-auth'.

How to handle Options Method?

How to Handle 2 method options to same view function ?
I must pair PUT with OPTIONS method but also must pair DELETE with OPTIONS method.
If I do that, there a clash with OPTIONS method because they refered different functions.

example code :
e.Path("GET", "/prospect", handler.Fetch)
e.Path("POST", "/prospect", handler.Store)

e.Path("PUT", "/prospect/:key", handler.Update)
e.Path("OPTIONS", "/prospect/:key", handler.Update)

e.Path("DELETE", "/prospect/:key", handler.Delete)
e.Path("OPTIONS", "/prospect/:key", handler.Delete)

How to resolve that ?

Unable to match URL with "/" ending

If server.RedirectTrailingSlash(false) is specified in the settings,
unable to access url ending with "/" .

package main
import (
"github.com/savsgio/atreugo"
)

func main() {
config := atreugo.Config{
Addr: "0.0.0.0:8080",
}
server := atreugo.New(config)
server.RedirectTrailingSlash(false)
server.GET("/hello/", func(ctx *atreugo.RequestCtx) error {
return ctx.TextResponse("Hello World")
})
if err := server.ListenAndServe(); err != nil {
panic(err)
}
}

localhost:8080/hello/ is not work

RedirectTrailingSlash undefined (type *atreugo.Atreugo has no field or method RedirectTrailingSlash)

RedirectTrailingSlash undefined (type *atreugo.Atreugo has no field or method RedirectTrailingSlash)

import "github.com/savsgio/atreugo/v8"

server := atreugo.New(atreugoConfig)
server. RedirectTrailingSlash(false)
$ go build
$ server.RedirectTrailingSlash undefined (type *atreugo.Atreugo has no field or method RedirectTrailingSlash)

atreugo version is:

github.com/savsgio/atreugo v5.4.0+incompatible
github.com/savsgio/atreugo/v8 v8.2.1

Example unix socket server for nginx

Hi, I liked your implementation of the http server and I'm wondering if it is possible to implement the server through FastCGI Unix Socket?

Or does it make no sense in the case of Go?

Readme provides wrong install instructions

Readme says to use the command go get github.com/savsgio/atreugo/v8
Should instead be go get github.com/savsgio/atreugo

package github.com/savsgio/atreugo/v8: cannot find package "github.com/savsgio/atreugo/v8" in any of: ```

How can I set the static config?

Our project requires static service, in fasthttp, we can set the config
fs := &fasthttp.FS{
Root: *dir,
IndexNames: []string{"index.html"},
GenerateIndexPages: *generateIndexPages,
Compress: *compress,
AcceptByteRange: *byteRange,
}
In atreugo, How can I set it up?
Can it be added in the config struct?

How to update the context in a middleware

Some libraries, such as opencensus take advantage of the context, for example storing http scoped request tags into the context and then recording metrics based on this saved context in downstream handlers.

If you'd like at ochttp, an opencensus plugin for net/http you'd find extensive usage of context.Context, including creating a new context and passing this context to the next handler downstream. I think this pattern is common.

My question is how can we simulate such behavior with atreugo/fasthttp. The Value method is not enough and so is the the SetUserValue (I have read the response to #48 but I thing that won't cut it).

We need a way to update the internal context.Context and store it in atreugo.RequestCtx, does that make sense? (is there a way to do that currently)

Thank you

how do you mock this with mockery or mockgen?

I want to try to use the standard library for mocking the interface, which is using mockgen.
but your package use tag versioning, which doesn't work with mockgen. I haven't try the mockery yet.
but i would like to know your solution instead of writing mock manually

here is the information about generating mock using mockgen
issue on mockgen issue 379

mockgen -destination=mocks/mock_user.go -source=internal/module/user/initiator.go
2020/01/07 17:24:53 Loading input failed: internal/module/user/initiator.go:54:6: failed parsing arguments: internal/module/user/initiator.go:54:12: unknown package "atreugo"

I am using "github.com/savsgio/atreugo/v10" for my http router
with interface

type Handler interface {
	Test(ctx *atreugo.RequestCtx) error
	CreateNewAccount(ctx *atreugo.RequestCtx) error
}

it seems that gomock doesn't recognize the tag versioning, so it results as panic because there is no atreugo inside of my imports.

Additional Information

  • gomock version: v1.3.1
  • golang version: go version go1.13.1 linux/amd64

Properly register middlewares when using NewGroupPath

Consider this :

	AuthRouter = Server.Router.NewGroupPath("/auth")

	// Authentication middleware
	Server.Router.UseBefore(func(ctx *atreugo.RequestCtx) error {
		token := ctx.Request.Header.Peek("Authorization")
		claims, err := jwt.ECDSACheck(token, jwtPublicKey)
		if err != nil || !claims.Valid(time.Now()) {
			ctx.JSONResponse(map[string]interface{}{
				"err": "You have been logged out. Login to continue",
			}, 401)
			return nil
		}

		ctx.SetUserValue("auth_username", claims.Subject)
		ctx.Next()
		return nil
	})

	UploadRouter = Server.Router.NewGroupPath("/upload")

AuthRouter & UploadRouter are exported and the paths are defined in another package.
So right now there are no paths ( or routes ) defined.
This makes the middleware register first causing it to run before any route.
I expected that the middleware would run if the route is anything other than /auth because of the order in which they were defined. But it appears that NewGroupPath has no effect in order of middleware registration. I don't think it's really helpful this way.

Support custom logger

Hey,

it would be great to support passing custom logger which implements basic log.Logger interface. And if it's possible completely disable logger inside atreugo server

Instrumenting servers / paths to add metrics doesn't work when view returns an error

I was trying to add instrumentation around the Atreugo server and/or around Paths.
I created a middleware and used UseBefore and UseAfter() to wire it, and the middle ware registers the processing time, response status, errors, etc.

The problem is, that when a view returns an error, the After middleware doesn't get called, and instead (I think) only the ErrorView is called.

This for me sounds like a really problematic flow as it prevents you from writing such middlewares unless I'm missing something here.

BTW, I saw a few closed tickets here about implementing something similar, but the code snippets in the tickets won't work, as the After middleware won't get called.

Is there a way to fix this?
Am I doing it wrong?

Cannot use TLS on single core servers

Sample config:

config := &atreugo.Config{
	Host:     "localhost",
	Port: 8445,
	TLSEnable: true,
	LogLevel: logger.DEBUG,
	CertFile: "./cert.pem",
	CertKey:  "./privkey.pem",
}

On single core server answer is:

2019/07/27 06:24:03 - atreugo - INFO - Listening on: http://127.0.0.1:80/

On >1 core server answer is:

2019/07/27 07:05:04 - atreugo - INFO - Updating address config with the new listener address
2019/07/27 07:05:04 - atreugo - INFO - Listening on: https://127.0.0.1:8445/

I guess this feature is somehow broken with listener_unix.go, where you rewriting existing config with default one, but if we made runtime.NumCPU() > 0 absolutely nothing changes.

Everything magically changes when we start to use more than one core. I really don't know where problem is.

I can not install

installation link is not working. "go get github.com/savsgio/atreugo/v11"

In viewFn, if setStatusCode must run afterfilters

In viewFn
if setStatusCode in response func must run afterfilters
if return err, statusCode must be set fasthttp.StatusInternalServerError

example: in login handler, after check username, if error, i hope return 401 and not run afterfilers

  1. use response to retrun, will run afterfilers
    return ctx.TextResponse("Unauthorized", 401)
  2. return err, but statucode is fasthttp.StatusInternalServerError
    return errors.New("Unauthorized")

My suggestion can modify the following function

if statusCode, err = execMiddlewares(actx, before); err == nil {
	if err = viewFn(actx); err != nil {
		statusCode = fasthttp.StatusInternalServerError
	} else {
		statusCode, err = execMiddlewares(actx, after)
	}
}

to

if statusCode, err = execMiddlewares(actx, before); err == nil {
	if err = viewFn(actx); err != nil {
	    if actx.Response.StatusCode != 0 {
			statusCode = actx.Response.StatusCode
		}
		statusCode = fasthttp.StatusInternalServerError
	} else {
		statusCode, err = execMiddlewares(actx, after)
	}
}

I can

ctx.SetStatusCode(401)
return errors.New("Unauthorized")

Implement direct return error code, but do not run afterfilters

It is recommended to set fasthttp.Server and fasthttprouter.Router directly via atruego

Atreugo is really a great web framework. In order to implement the function of custom static file service, savsgio also specially modified the code of fasthttprouter. It can be seen that savsgio is really responsible for the code. And setting parameters in the function is more flexible than setting the parameters in cfg, which is a better solution.
These days have been learning the source code of atreugo, atreugo integrates a lot of functions required by web development, fasthttp, reuseport, fasthttprouter, logger, json.

config := &atreugo.Config{
	Host: "0.0.0.0",
	Port: 8000,
}
server := atreugo.New(config)

For the above code, I understand that when set config, run atreugo.New (config), in fact, according to the parameters set in the config, initialize fasthttp.Server and fasthttprouter.Router
However, in atreugo, the parameters that can be adjusted are only the fields in config, and if fasthttp.Server and fasthttprouter.Router have new function parameters, they need to be adjusted by atreugo.
I suggest that you can directly adjust the initial parameters of fasthttp.Server and fasthttprouter.Router in atreugo, for example:

config := &atreugo.Config{
	Host: "0.0.0.0",
	Port: 8000,
}
server := atreugo.New(config)
server.Router.Router.RedirectTrailingSlash = false
server.Server.NoDefaultContentType = true

This allows you to fine-tune the settings of fasthttp.Server and fasthttprouter.Router, but now atreugo, these are not externally accessible data.

Of course, the drawback is that there may be a setting error, or even destroy the operation of atreugo, but this can be explained in the manual. It is recommended to use atreugo.Config to initialize during development.
If you are familiar with atreugo, fasthttp, fasthttprouter, and need more subtle settings, you can use atreugo to directly set the fasthttp.Server and fasthttprouter.Router parameters for initialization.

Unsupported path parameters with root / prefix

These are the routes I need:

  1. GET /:code
  2. POST /shorten
  3. POST /expand

However, I get the following error:

panic: 'shorten' in new path '/shorten' conflicts with existing wildcard ':path' in existing prefix '/:path'

goroutine 1 [running]:
github.com/fasthttp/router.(*node).addRoute(0xc0000b0550, 0x16f28bc, 0x8, 0xc00020b800)
	~/go/pkg/mod/github.com/fasthttp/[email protected]/tree.go:172 +0x352
github.com/fasthttp/router.(*Router).Handle(0xc0000acc00, 0x16f24fc, 0x7, 0x16f28bc, 0x8, 0xc00020b800)
	~/go/pkg/mod/github.com/fasthttp/[email protected]/router.go:285 +0x297
github.com/savsgio/atreugo/v10.(*Router).init(0xc0000ca790)
	~/go/pkg/mod/github.com/savsgio/atreugo/[email protected]/router.go:98 +0x3fe
github.com/savsgio/atreugo/v10.(*Atreugo).Serve(0xc0000ed7c0, 0x186e1a0, 0xc0000a76c0, 0x0, 0x0)
	~/go/pkg/mod/github.com/savsgio/atreugo/[email protected]/atreugo.go:173 +0x8d
github.com/savsgio/atreugo/v10.(*Atreugo).ListenAndServe(0xc0000ed7c0, 0x186e420, 0xc0000a76a0)
	~/go/pkg/mod/github.com/savsgio/atreugo/[email protected]/atreugo.go:250 +0x7c
main.main()
	$MY_PROJECT_PATH/shortener/app/main.go:50 +0x509
exit status 2

A page for the framework would be nice to have

I just stumbled upon this framework and I see it as a really good click it would be nice to have a webpage something similar to echo https://echo.labstack.com/ like getting started i think they have the best documentation among any framework I have seen.

Also, I know that fasthttprouter still lacks the support for http2 is http2 supported in this even though like it's built on top of it because lots of people including me will be hesitant to adopt it for a full-blown production use

AttachContext is not transitive

I realized there is another problem with AttachContext and friends.
Attaching a context and then creating a new context and attaching it, makes RequestCtx forget the previous attached context, which to me seems like a breach of the context contract

Example test:

func Test_ValueTransitive(t *testing.T) {
	ctx := acquireRequestCtx(new(fasthttp.RequestCtx))

	ctx2 := context.WithValue(ctx, "k2", "v2")
	ctx.AttachContext(ctx2)
	if v := ctx.Value("k2"); v != "v2" {
		t.Errorf("Value() key '%s' == %v, want %v", "k2", v, "v2")
	}

	ctx3 := context.WithValue(ctx, "k3", "v3")
	ctx.AttachContext(ctx3)
	// Should remember *both* k2 and k3
	if v := ctx.Value("k2"); v != "v2" {
		t.Errorf("Value() key '%s' == %v, want %v", "k2", v, "v2") // THIS FAILS, but should not
	}
	if v := ctx.Value("k3"); v != "v3" {
		t.Errorf("Value() key '%s' == %v, want %v", "k3", v, "v3")
	}
}

I'll send a pull request with fix suggestion soon

URL encoded / breaks routing

Trying to pass a url encoded / character as a url parameter. This breaks routing. Appears to be an issue with most fasthttp routing frameworks.

Just add %2F to a url parameter and the routing won't work anymore

[Error] Wildcard conflict serving static files

How can we serve static files along with custom paths?

panic: wildcard route '*filepath' conflicts with existing children in path '/*filepath'
package main

import (
	"fmt"

	"github.com/savsgio/atreugo"
)

func main() {
  // App settings
	app := atreugo.New(&atreugo.Config{
		Addr: "0.0.0.0:8000",
	})
  // Paths
	app.Path("GET", "/api/user/:name", func(ctx *atreugo.RequestCtx) error {
		name := ctx.UserValue("name")
		return ctx.TextResponse(fmt.Sprintf("Name: %s", name))
	})
  // If no match, try to serve static
	app.Static("/", "./static")
  // Run server
	err := app.ListenAndServe()
	if err != nil {
		panic(err)
	}
}

PS: And how can we handle custom 404 handling or display a different message than Directory index is forbidden

How to prevent execution of a `view` using a middleware without error

I'm thinking about to implement a caching middleware, but I don't see a way to return an answer from an before middleware and prevent execution of the view without causing an error.

May be the further chain shouldn't be executed if a middleware returned a non-zero statuscode.

Have document for explain about your framework?

I confuse and don't know how to getting start well in your framework.

It will good if you have github.io for explain your framework about benefit feature and more.

And in readme, I think it should to have List of current feature and Todo feature for roadmap in the future.

For ensure developer can walk with you in long-term.

Endless recursion with AttachContext

I'm sorry, but there seems to be an issue with the new AttachContext function.

When you attach a context that contains the original request context and then call Value with a key that does not exist, you get an endless recursion (stack overflow).

Example:

func Test_ValueEndlessRecursion(t *testing.T) {
	ctx := acquireRequestCtx(new(fasthttp.RequestCtx))
	ctx.AttachContext(context.WithValue(ctx, "yyy", "zzz"))
	ctx.Value("xxx")
}

Or even a simpler example:

func Test_ValueEndlessRecursion(t *testing.T) {
	ctx := acquireRequestCtx(new(fasthttp.RequestCtx))
	ctx.AttachContext(ctx)
	ctx.Value("xxx")
}

Result:

=== RUN   Test_ValueEndlessRecursion
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x14f95c7, 0xe)
	/usr/local/go/src/runtime/panic.go:774 +0x72
runtime.newstack()
	/usr/local/go/src/runtime/stack.go:1046 +0x6e9
runtime.morestack()
	/usr/local/go/src/runtime/asm_amd64.s:449 +0x8f

goroutine 50 [running]:
runtime.getitab(0x14834c0, 0x1498000, 0x1, 0xc0001d8030)
	/usr/local/go/src/runtime/iface.go:33 +0x37b fp=0xc0201dc358 sp=0xc0201dc350 pc=0x100ae6b
runtime.assertE2I2(0x14834c0, 0x1498000, 0xc0001d8000, 0x1498000, 0xc0001d8000, 0x0)
	/usr/local/go/src/runtime/iface.go:487 +0x43 fp=0xc0201dc388 sp=0xc0201dc358 pc=0x100bfe3
github.com/savsgio/atreugo/v10.(*RequestCtx).AttachedContext(0xc0001d6000, 0x14478c0, 0x15873d0)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:64 +0x77 fp=0xc0201dc3c8 sp=0xc0201dc388 pc=0x13e5c97
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:79 +0x64 fp=0xc0201dc400 sp=0xc0201dc3c8 pc=0x13e5d44
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc438 sp=0xc0201dc400 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc470 sp=0xc0201dc438 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc4a8 sp=0xc0201dc470 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc4e0 sp=0xc0201dc4a8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc518 sp=0xc0201dc4e0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc550 sp=0xc0201dc518 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc588 sp=0xc0201dc550 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc5c0 sp=0xc0201dc588 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc5f8 sp=0xc0201dc5c0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc630 sp=0xc0201dc5f8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc668 sp=0xc0201dc630 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc6a0 sp=0xc0201dc668 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc6d8 sp=0xc0201dc6a0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc710 sp=0xc0201dc6d8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc748 sp=0xc0201dc710 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc780 sp=0xc0201dc748 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc7b8 sp=0xc0201dc780 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc7f0 sp=0xc0201dc7b8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc828 sp=0xc0201dc7f0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc860 sp=0xc0201dc828 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc898 sp=0xc0201dc860 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc8d0 sp=0xc0201dc898 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc908 sp=0xc0201dc8d0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc940 sp=0xc0201dc908 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc978 sp=0xc0201dc940 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dc9b0 sp=0xc0201dc978 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dc9e8 sp=0xc0201dc9b0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dca20 sp=0xc0201dc9e8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dca58 sp=0xc0201dca20 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dca90 sp=0xc0201dca58 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dcac8 sp=0xc0201dca90 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dcb00 sp=0xc0201dcac8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dcb38 sp=0xc0201dcb00 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dcb70 sp=0xc0201dcb38 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dcba8 sp=0xc0201dcb70 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dcbe0 sp=0xc0201dcba8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dcc18 sp=0xc0201dcbe0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dcc50 sp=0xc0201dcc18 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dcc88 sp=0xc0201dcc50 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dccc0 sp=0xc0201dcc88 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dccf8 sp=0xc0201dccc0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dcd30 sp=0xc0201dccf8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dcd68 sp=0xc0201dcd30 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dcda0 sp=0xc0201dcd68 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dcdd8 sp=0xc0201dcda0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dce10 sp=0xc0201dcdd8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dce48 sp=0xc0201dce10 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dce80 sp=0xc0201dce48 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dceb8 sp=0xc0201dce80 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dcef0 sp=0xc0201dceb8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dcf28 sp=0xc0201dcef0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dcf60 sp=0xc0201dcf28 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dcf98 sp=0xc0201dcf60 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dcfd0 sp=0xc0201dcf98 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd008 sp=0xc0201dcfd0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd040 sp=0xc0201dd008 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd078 sp=0xc0201dd040 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd0b0 sp=0xc0201dd078 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd0e8 sp=0xc0201dd0b0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd120 sp=0xc0201dd0e8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd158 sp=0xc0201dd120 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd190 sp=0xc0201dd158 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd1c8 sp=0xc0201dd190 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd200 sp=0xc0201dd1c8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd238 sp=0xc0201dd200 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd270 sp=0xc0201dd238 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd2a8 sp=0xc0201dd270 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd2e0 sp=0xc0201dd2a8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd318 sp=0xc0201dd2e0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd350 sp=0xc0201dd318 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd388 sp=0xc0201dd350 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd3c0 sp=0xc0201dd388 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd3f8 sp=0xc0201dd3c0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd430 sp=0xc0201dd3f8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd468 sp=0xc0201dd430 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd4a0 sp=0xc0201dd468 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd4d8 sp=0xc0201dd4a0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd510 sp=0xc0201dd4d8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd548 sp=0xc0201dd510 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd580 sp=0xc0201dd548 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd5b8 sp=0xc0201dd580 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd5f0 sp=0xc0201dd5b8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd628 sp=0xc0201dd5f0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd660 sp=0xc0201dd628 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd698 sp=0xc0201dd660 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd6d0 sp=0xc0201dd698 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd708 sp=0xc0201dd6d0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd740 sp=0xc0201dd708 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd778 sp=0xc0201dd740 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd7b0 sp=0xc0201dd778 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd7e8 sp=0xc0201dd7b0 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd820 sp=0xc0201dd7e8 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd858 sp=0xc0201dd820 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd890 sp=0xc0201dd858 pc=0x13e5d71
context.(*valueCtx).Value(0xc0001d8000, 0x14478c0, 0x15873d0, 0x0, 0x0)
	/usr/local/go/src/context/context.go:520 +0x58 fp=0xc0201dd8c8 sp=0xc0201dd890 pc=0x10ee038
github.com/savsgio/atreugo/v10.(*RequestCtx).Value(0xc0001d6000, 0x14478c0, 0x15873d0, 0xc0001d8000, 0x1)
	/Users/ran/dev/src/github.com/savsgio/atreugo/context.go:80 +0x91 fp=0xc0201dd900 sp=0xc0201dd8c8 pc=0x13e5d71
...additional frames elided...
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:960 +0x350

goroutine 1 [chan receive]:
testing.(*T).Run(0xc0001c4100, 0x14fe808, 0x1a, 0x1514110, 0x1085601)
	/usr/local/go/src/testing/testing.go:961 +0x377
testing.runTests.func1(0xc0001c4000)
	/usr/local/go/src/testing/testing.go:1202 +0x78
testing.tRunner(0xc0001c4000, 0xc0001bddc0)
	/usr/local/go/src/testing/testing.go:909 +0xc9
testing.runTests(0xc0000b8b80, 0x18b56c0, 0x26, 0x26, 0x0)
	/usr/local/go/src/testing/testing.go:1200 +0x2a7
testing.(*M).Run(0xc000126200, 0x0)
	/usr/local/go/src/testing/testing.go:1117 +0x176
main.main()
	_testmain.go:124 +0x135

goroutine 34 [syscall]:
os/signal.signal_recv(0x0)
	/usr/local/go/src/runtime/sigqueue.go:144 +0x96
os/signal.loop()
	/usr/local/go/src/os/signal/signal_unix.go:23 +0x22
created by os/signal.init.0
	/usr/local/go/src/os/signal/signal_unix.go:29 +0x41

Process finished with exit code 1

Use atreugo and autocert

I want to use atreugo to server https with autocert to generate certs, I use the example of autocert, it works.

m := &autocert.Manager{
		Cache:      autocert.DirCache("secret-dir"),
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist("shuoyu.wang"),
	}
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "1")
})
s := &http.Server{
	Addr:      ":https",
	TLSConfig: m.TLSConfig(),
	Handler:   mux,
}
s.ListenAndServeTLS("", "")

switching to atreugo

	config := atreugo.Config{}
	server := atreugo.New(config)
	server.GET("/", func(ctx *atreugo.RequestCtx) error {
		return ctx.TextResponse("1")
	})

	l, err := net.Listen("tcp", ":443")
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()

	listener := tls.NewListener(l, m.TLSConfig())

	if err := server.Serve(listener); err != nil {
		panic(err)
	}

or just

	if err := server.Serve(m.Listener()); err != nil {
		panic(err)
	}

Chrome shows "ERR_HTTP2_PROTOCOL_ERROR"
curl -v shows

  • http2 error: Remote peer returned unexpected data while we expected SETTINGS frame. Perhaps, peer does not support HTTP/2 properly.
  • Curl_http_done: called premature == 1
  • Closing connection 0

Setting user context

You have jwt intermediate in the examples, but you set everything there in cookies. The question is, how will it be correctly installed in the context and then received from there?

Here, for example, looks like regular net/http:

var ExampleJWTNet = func(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		//...
		// find by JWT token

		user := &User{100, false} // for example

		ctx := context.WithValue(r.Context(), "user", user)
		r = r.WithContext(ctx)
		next.ServeHTTP(w, r)
	})
}

and further it was possible to get this way:

func(w http.ResponseWriter, r *http.Request) Example {
    user := r.Context().Value("user")
}

I understand correctly that you need to use UserValue?

func JWTAuthorization(ctx *atreugo.RequestCtx) (int, error) {
	logger.Info("JWT Authorization")

	if authenticate(ctx) {
		logger.Info("User successfully Logged In")
                ctx.SetUserValue("key", "VALUE USER")
	}

	return 200, nil
}

Recommend atreugo increase beforeroute viewfn

I understand, httpserver before the router, should be able to increase the processing steps.
Before the project using nginx + lua, before the request is routed, can do ip whitelist , ip blacklist, waf, etc.
But middleware is operating after routing.

Hi, Savsgio, how to add CORS middleware?

There is no fasthttp.RequestHandler param in atreugo ListenAndServe function
fasthttpcors

server.POST('/', func(ctx *atreugo.RequestCtx) error {
	// Set Origin
	ctx.Response.Header.Set("Access-Control-Allow-Origin", "*")
...
})

not working too

Thanks.

I implemented a hot restart on fasthttp, but it cannot be implemented on atreugo because I can't access net.listen of atreugo.server


Imitate the code for this page, I should implement a hot restart on fasthttp

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"github.com/fasthttp/router"
	"github.com/valyala/fasthttp"
	"github.com/valyala/fasthttp/reuseport"
)

type listener struct {
	Addr     string `json:"addr"`
	FD       int    `json:"fd"`
	Filename string `json:"filename"`
}

func importListener(addr string) (net.Listener, error) {
	// Extract the encoded listener metadata from the environment.
	listenerEnv := os.Getenv("LISTENER")
	if listenerEnv == "" {
		return nil, fmt.Errorf("unable to find LISTENER environment variable")
	}

	// Unmarshal the listener metadata.
	var l listener
	err := json.Unmarshal([]byte(listenerEnv), &l)
	if err != nil {
		return nil, err
	}
	if l.Addr != addr {
		return nil, fmt.Errorf("unable to find listener for %v", addr)
	}

	// The file has already been passed to this process, extract the file
	// descriptor and name from the metadata to rebuild/find the *os.File for
	// the listener.
	listenerFile := os.NewFile(uintptr(l.FD), l.Filename)
	if listenerFile == nil {
		return nil, fmt.Errorf("unable to create listener file: %v", err)
	}
	defer listenerFile.Close()

	// Create a net.Listener from the *os.File.
	ln, err := net.FileListener(listenerFile)
	if err != nil {
		return nil, err
	}

	return ln, nil
}

func createListener(addr string) (net.Listener, error) {
	//ln, err := net.Listen("tcp", addr)
	ln, err := reuseport.Listen("tcp6", addr)
	if err != nil {
		return nil, err
	}

	return ln, nil
}

func createOrImportListener(addr string) (net.Listener, error) {
	// Try and import a listener for addr. If it's found, use it.
	ln, err := importListener(addr)
	if err == nil {
		fmt.Printf("Imported listener file descriptor for %v.\n", addr)
		return ln, nil
	}

	// No listener was imported, that means this process has to create one.
	ln, err = createListener(addr)
	if err != nil {
		return nil, err
	}

	fmt.Printf("Created listener file descriptor for %v.\n", addr)
	return ln, nil
}

func getListenerFile(ln net.Listener) (*os.File, error) {
	switch t := ln.(type) {
	case *net.TCPListener:
		return t.File()
	case *net.UnixListener:
		return t.File()
	}
	return nil, fmt.Errorf("unsupported listener: %T", ln)
}

func forkChild(addr string, ln net.Listener) (*os.Process, error) {
	// Get the file descriptor for the listener and marshal the metadata to pass
	// to the child in the environment.
	lnFile, err := getListenerFile(ln)
	if err != nil {
		return nil, err
	}
	defer lnFile.Close()
	l := listener{
		Addr:     addr,
		FD:       3,
		Filename: lnFile.Name(),
	}
	listenerEnv, err := json.Marshal(l)
	if err != nil {
		return nil, err
	}

	// Pass stdin, stdout, and stderr along with the listener to the child.
	files := []*os.File{
		os.Stdin,
		os.Stdout,
		os.Stderr,
		lnFile,
	}

	// Get current environment and add in the listener to it.
	environment := append(os.Environ(), "LISTENER="+string(listenerEnv))

	// Get current process name and directory.
	execName, err := os.Executable()
	if err != nil {
		return nil, err
	}
	execDir := filepath.Dir(execName)

	// Spawn child process.
	p, err := os.StartProcess(execName, []string{execName}, &os.ProcAttr{
		Dir:   execDir,
		Env:   environment,
		Files: files,
		Sys:   &syscall.SysProcAttr{},
	})
	if err != nil {
		return nil, err
	}

	return p, nil
}

func waitForSignals(addr string, ln net.Listener, server *fasthttp.Server) error {
	signalCh := make(chan os.Signal, 1024)
	signal.Notify(signalCh, syscall.SIGHUP, syscall.SIGUSR2, syscall.SIGINT, syscall.SIGQUIT)
	for {
		select {
		case s := <-signalCh:
			fmt.Printf("%v signal received.\n", s)
			switch s {
			case syscall.SIGHUP:
				// Fork a child process.
				p, err := forkChild(addr, ln)
				if err != nil {
					fmt.Printf("Unable to fork child: %v.\n", err)
					continue
				}
				fmt.Printf("Forked child %v.\n", p.Pid)

				// Return any errors during shutdown.
				return server.Shutdown()
			case syscall.SIGUSR2:
				// Fork a child process.
				p, err := forkChild(addr, ln)
				if err != nil {
					fmt.Printf("Unable to fork child: %v.\n", err)
					continue
				}

				// Print the PID of the forked process and keep waiting for more signals.
				fmt.Printf("Forked child %v.\n", p.Pid)
			case syscall.SIGINT, syscall.SIGQUIT:
				// Return any errors during shutdown.
				return server.Shutdown()
			}
		}
	}
}

func handler(ctx *fasthttp.RequestCtx) {
	fmt.Fprintf(ctx, "Hello from %v!\n", os.Getpid())
}

func test(ctx *fasthttp.RequestCtx) {
	fmt.Fprintf(ctx, "Test from %v!\n", os.Getpid())
}

func web(ctx *fasthttp.RequestCtx) {
	fmt.Fprintf(ctx, "WEB from %v!\n", os.Getpid())
}

func startServer(ln net.Listener) *fasthttp.Server {
	r := router.New()
	r.GET("/hello", handler)
	//r.GET("/test", test)
	//r.GET("/web", web)

	httpServer := &fasthttp.Server{
		Name: "fast",
		Handler: r.Handler,
	}
	go httpServer.Serve(ln)

	return httpServer
}

var addr string
var ln net.Listener
var server *fasthttp.Server

func main() {
	// Parse command line flags for the address to listen on.
	flag.StringVar(&addr, "addr", ":8080", "Address to listen on.")

	// Create (or import) a net.Listener and start a goroutine that runs
	// a HTTP server on that net.Listener.
	var err error
	ln, err = createOrImportListener(addr)
	if err != nil {
		fmt.Printf("Unable to create or import a listener: %v.\n", err)
		os.Exit(1)
	}
	server = startServer(ln)

	// Wait for signals to either fork or quit.
	err = waitForSignals(addr, ln, server)
	if err != nil {
		fmt.Printf("Exiting: %v\n", err)
		return
	}
	fmt.Printf("Exiting.\n")
}

when I :

  1. add r.GET("/test", test), and recompile the program
  2. Replace the old program file with the newly compiled program file
  3. kill -1
    the /test can be accessed

When I plan to use this code to atreugo, but find the net.listen of atreugo.server can't be use.

New release, next to v8.2.1

Hi, savsgio!
Thank you for effort, very attractive changes since v8.2.1 was published.
Can you please create new release/tag v8.2.2, to use it from go mod more simple?
Thanks!

Can add the StaticCustomWithFilters function?

Please forgive my request because the project(static file server) needs ip whitelist and add Cache-Control header.
Atreugo's static file functions are not middlewares and filter.
I am going to write a StaticCustom function with filter, but find i can't use the router.ServeFilesCustom().

how do you wrap the routers to get all registered paths?

I would like to know how you can wrap the router to get the data from any path being requested.
let's say I have 3 paths registered. one of it is /test.
when i hit localhost:port/test on my browser, i know exactly what path is being called, what method it is and how long the execution time is for any handler.

HeaderReceived it can increase the Config struct in?

Current projects include the business logic and upload.
Because of the need to upload large files, I must modify the values ​​in the config MaxRequestBodySize to 100M in atreugo.
But the MaxRequestBodySize of server struct is global set, I only wish MaxRequestBodySize can be modify in upload url.

HeaderReceived it can increase the Config struct in?
I can set this value of ReadTimeout WriteTimeout MaxRequestBodySize in accordance with url path.

Problem with trailing "/" when optional url param is empty (11 version) - 308 redirect

Have problem with version 11 - new router redirects with 308 code request like /path to /path/, if route /path/{param?} exists

External servers that call me in prod (like Google push ), don't follow redirects, I have to write both /path and /path/{param}, without optional parameters support.

Is it possible to add also the old fasthttp router, or solve this issue somehow?

Can I unify the style of response in atreugo?

In the restful api project, i try to unify the style of response.
If the middlewares or filters return err, actx.Error() will response the body, i can't modify to json response.
I want

HTTP/1.1 401 Unauthorized
Server: atreugo
Date: Tue, 27 Aug 2019 03:38:40 GMT
Content-Type: application/json
Content-Length: 28

{"code":401,"msg":"Unauthorized"}

But middlewares or filters return err, the response is

HTTP/1.1 401 Unauthorized
Server: atreugo
Date: Tue, 27 Aug 2019 03:39:37 GMT
Content-Type: text/plain
Content-Length: 12

Unauthorized

Is it possible to run "after" middleware even when "before" middleware exits error?

I'm trying to add an "after" middleware that runs on every request for stats purposes (the example below simply logs the response status code).

But I also have an authenticating "before" middleware, and when that blocks a request, my "after" middleware doesn't run.

Is there a way to make this work?

func main() {
	server := atreugo.New(&atreugo.Config{Addr: "0.0.0.0:8080"})

	// global "after" middleware to log response code
	server.UseAfter(func(ctx *atreugo.RequestCtx) error {
		ctx.Logger().Printf("status code: %d", ctx.Response.StatusCode())
		return ctx.Next()
	})

	// unauthorized in the handler - logging middleware runs
	server.GET("/401", func(ctx *atreugo.RequestCtx) error {
		return ctx.ErrorResponse(nil, fasthttp.StatusUnauthorized)
	})

	// authorized by Before middleware - logging middleware DOES run
	server.NewGroupPath("/unprotected").UseBefore(func(ctx *atreugo.RequestCtx) error {
		return ctx.Next()
	}).GET("/200", func(ctx *atreugo.RequestCtx) error {
		return ctx.ErrorResponse(nil, fasthttp.StatusOK)
	})

	// unauthorized by Before middleware - logging middleware DOES NOT run
	server.NewGroupPath("/protected").UseBefore(func(ctx *atreugo.RequestCtx) error {
		return ctx.ErrorResponse(nil, fasthttp.StatusUnauthorized)
	}).GET("/200", func(ctx *atreugo.RequestCtx) error {
		return ctx.ErrorResponse(nil, fasthttp.StatusOK)
	})

	log.Fatal(server.ListenAndServe())
}

Can atreugo be hot upgrade?

I am going to implement a restful api with atreugo.
In the godoc, I saw the ServeGracefully method.
If atreugo can achieve hot upgrade, How do I change the code?
I used to develop with nginx and php, not very familiar with the upgrade method of go program.
Can you give an example?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.