Giter VIP home page Giter VIP logo

router's Introduction

Router

Test status Coverage Status Go Report Card GoDev GitHub release

Router is a lightweight high performance HTTP request router (also called multiplexer or just mux for short) for fasthttp.

This router is optimized for high performance and a small memory footprint. It scales well even with very long paths and a large number of routes. A compressing dynamic trie (radix tree) structure is used for efficient matching.

Based on julienschmidt/httprouter.

Features

Best Performance: Router is one of the fastest go web frameworks in the go-web-framework-benchmark. Even faster than httprouter itself.

  • 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

See below for technical details of the implementation.

Only explicit matches: With other routers, like http.ServeMux, a requested URL path could match multiple patterns. Therefore they have some awkward pattern priority rules, like longest match or first registered, first matched. By design of this router, a request can only match exactly one or no route. As a result, there are also no unintended matches, which makes it great for SEO and improves the user experience.

Stop caring about trailing slashes: Choose the URL style you like, the router automatically redirects the client if a trailing slash is missing or if there is one extra. Of course it only does so, if the new path has a handler. If you don't like it, you can turn off this behavior.

Path auto-correction: Besides detecting the missing or additional trailing slash at no extra cost, the router can also fix wrong cases and remove superfluous path elements (like ../ or //). Is CAPTAIN CAPS LOCK one of your users? Router can help him by making a case-insensitive look-up and redirecting him to the correct URL.

Parameters in your routing pattern: Stop parsing the requested URL path, just give the path segment a name and the router delivers the dynamic value to you. Because of the design of the router, path parameters are very cheap.

Zero Garbage: The matching and dispatching process generates zero bytes of garbage. In fact, the only heap allocations that are made, is by building the slice of the key-value pairs for path parameters. If the request path contains no parameters, not a single heap allocation is necessary.

No more server crashes: You can set a Panic handler to deal with panics occurring during handling a HTTP request. The router then recovers and lets the PanicHandler log what happened and deliver a nice error page.

Perfect for APIs: The router design encourages to build sensible, hierarchical RESTful APIs. Moreover it has builtin native support for OPTIONS requests and 405 Method Not Allowed replies.

Of course you can also set custom NotFound and MethodNotAllowed handlers and serve static files.

Usage

This is just a quick introduction, view the GoDoc for details:

Let's start with a trivial example:

package main

import (
	"fmt"
	"log"

	"github.com/fasthttp/router"
	"github.com/valyala/fasthttp"
)

func Index(ctx *fasthttp.RequestCtx) {
	ctx.WriteString("Welcome!")
}

func Hello(ctx *fasthttp.RequestCtx) {
	fmt.Fprintf(ctx, "Hello, %s!\n", ctx.UserValue("name"))
}

func main() {
	r := router.New()
	r.GET("/", Index)
	r.GET("/hello/{name}", Hello)

	log.Fatal(fasthttp.ListenAndServe(":8080", r.Handler))
}

Named parameters

As you can see, {name} is a named parameter. The values are accessible via RequestCtx.UserValues. You can get the value of a parameter by using the ctx.UserValue("name").

Named parameters only match a single path segment:

Pattern: /user/{user}

 /user/gordon                     match
 /user/you                        match
 /user/gordon/profile             no match
 /user/                           no match

Pattern with suffix: /user/{user}_admin

 /user/gordon_admin               match
 /user/you_admin                  match
 /user/you                        no match
 /user/gordon/profile             no match
 /user/gordon_admin/profile       no match
 /user/                           no match

Optional parameters

If you need define an optional parameters, add ? at the end of param name. {name?}

Regex validation

If you need define a validation, you could use a custom regex for the paramater value, add :<regex> after the name. For example: {name:[a-zA-Z]{5}}.

Optional parameters and regex validation are compatibles, only add ? between the name and the regex. For example: {name?:[a-zA-Z]{5}}.

Catch-All parameters

The second type are catch-all parameters and have the form {name:*}. Like the name suggests, they match everything. Therefore they must always be at the end of the pattern:

Pattern: /src/{filepath:*}

 /src/                     match
 /src/somefile.go          match
 /src/subdir/somefile.go   match

How does it work?

The router relies on a tree structure which makes heavy use of common prefixes, it is basically a compact prefix tree (or just Radix tree). Nodes with a common prefix also share a common parent. Here is a short example what the routing tree for the GET request method could look like:

Priority   Path             Handle
9          \                 *<1>
3          ├s                nil
2          |├earch\          *<2>
1          |└upport\         *<3>
2          ├blog\            *<4>
1          |    └{post}      nil
1          |          └\     *<5>
2          ├about-us\        *<6>
1          |        └team\   *<7>
1          └contact\         *<8>

Every *<num> represents the memory address of a handler function (a pointer). If you follow a path trough the tree from the root to the leaf, you get the complete route path, e.g \blog\{post}\, where {post} is just a placeholder (parameter) for an actual post name. Unlike hash-maps, a tree structure also allows us to use dynamic parts like the {post} parameter, since we actually match against the routing patterns instead of just comparing hashes. [As benchmarks show][benchmark], this works very well and efficient.

Since URL paths have a hierarchical structure and make use only of a limited set of characters (byte values), it is very likely that there are a lot of common prefixes. This allows us to easily reduce the routing into ever smaller problems. Moreover the router manages a separate tree for every request method. For one thing it is more space efficient than holding a method->handle map in every single node, for another thing is also allows us to greatly reduce the routing problem before even starting the look-up in the prefix-tree.

For even better scalability, the child nodes on each tree level are ordered by priority, where the priority is just the number of handles registered in sub nodes (children, grandchildren, and so on..). This helps in two ways:

  1. Nodes which are part of the most routing paths are evaluated first. This helps to make as much routes as possible to be reachable as fast as possible.
  2. It is some sort of cost compensation. The longest reachable path (highest cost) can always be evaluated first. The following scheme visualizes the tree structure. Nodes are evaluated from top to bottom and from left to right.
├------------
├---------
├-----
├----
├--
├--
└-

Why doesn't this work with http.Handler?

Because fasthttp doesn't provide http.Handler. See this description.

Fasthttp works with RequestHandler functions instead of objects implementing Handler interface. So a Router provides a Handler interface to implement the fasthttp.ListenAndServe interface.

Just try it out for yourself, the usage of Router is very straightforward. The package is compact and minimalistic, but also probably one of the easiest routers to set up.

Where can I find Middleware X?

This package just provides a very efficient request router with a few extra features. The router is just a fasthttp.RequestHandler, you can chain any fasthttp.RequestHandler compatible middleware before the router. Or you could just write your own, it's very easy!

Have a look at these middleware examples:

Chaining with the NotFound handler

NOTE: It might be required to set Router.HandleMethodNotAllowed to false to avoid problems.

You can use another fasthttp.RequestHandler, for example another router, to handle requests which could not be matched by this router by using the Router.NotFound handler. This allows chaining.

Static files

The NotFound handler can for example be used to serve static files from the root path / (like an index.html file along with other assets):

// Serve static files from the ./public directory
r.NotFound = fasthttp.FSHandler("./public", 0)

But this approach sidesteps the strict core rules of this router to avoid routing problems. A cleaner approach is to use a distinct sub-path for serving files, like /static/{filepath:*} or /files/{filepath:*}.

Web Frameworks based on Router

If the Router is a bit too minimalistic for you, you might try one of the following more high-level 3rd-party web frameworks building upon the Router package:

router's People

Contributors

1046102779 avatar bbrodriges avatar buaazp avatar chaahk avatar da-z avatar eaglerayp avatar hawell avatar javierprovecho avatar jjeffery avatar julienschmidt avatar kamronbatman avatar kimkit avatar kirilldanshin avatar kokizzu avatar lhigueragamboa avatar markarts avatar mhor avatar milesdong avatar nothingmuch avatar oov avatar perpetual-hydrofoil avatar peterldowns avatar philippfranke avatar preetam avatar readmecritic avatar rogpeppe avatar sashayakovtseva avatar savsgio avatar tcyrus avatar zxfishhack 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

router's Issues

gotils is gpl

Hello, this repo use github.com/savsgio/gotils which is gpl3. Can you look at replacing with a compatible license for fasthttp/router? Thanks.

ServeFilesCustom responds for a long time when requesting a non existent file

  1. I need to make a route for /assets/. It works quickly if file exists, but if not it takes 2 seconds to response 404 in a folder that contains only 2 files. Why so long (testing on Windows 10)? I'm afraid it is a way for DOS attacks. Same request - again 2 seconds for 404 response.
r.ServeFilesCustom("/assets/{filepath:*}", &fasthttp.FS{
		Root:         "./assets",
		PathNotFound: NotFound, // here I can handle and LOG it if needed
		Compress:     true,
	})

And the system outputs to console the message:

2020/09/04 10:20:42 5.411 #0000000100000002 - 127.0.0.1:8090<->127.0.0.1:58104 
- GET http://test.local:8080/assets/js/asfgsf - cannot open file "./assets/js/asfgsf": open ./assets/js/asfgsf: The system cannot find the file specified.

Is there a way to suppress this message? What's it for in the console? This is only a useless waste of resources.

  1. http://test.local:8080/assets/////js////jquery-3.4.1.min.js
    stays with redundant slashes in new release v1.3.2

"path must begin with '/' in path ''" when using regex validation

Hey^^

I use the following line to achieve a regex validation on a path:
router.GET("/{filepath:^(?!api).*}", frontend.RequestHandler)

I want that this route only applies if the path does not begin with api. My problem is that the program panics:

panic: path must begin with '/' in path ''

goroutine 1 [running]:
github.com/fasthttp/router/radix.panicf(...)
        C:/Users/lukas/go/pkg/mod/github.com/fasthttp/[email protected]/radix/utils.go:13
github.com/fasthttp/router/radix.(*Tree).Add(0xc0000a8070, 0xea52fc, 0x0, 0xc0000a8050)
        C:/Users/lukas/go/pkg/mod/github.com/fasthttp/[email protected]/radix/tree.go:24 +0x44a
github.com/fasthttp/router.(*Router).Handle(0xc0000ac000, 0xe9e85a, 0x3, 0xea52fc, 0x18, 0xc0000a8050)
        C:/Users/lukas/go/pkg/mod/github.com/fasthttp/[email protected]/router.go:198 +0x1f3
github.com/fasthttp/router.(*Router).GET(...)
        C:/Users/lukas/go/pkg/mod/github.com/fasthttp/[email protected]/router.go:74

Add wildcard for `method` argument in Handle ?

Hi 👋

Proposal

What do you think about adding the possibility to pass a wildcard to the method argument in the Handle function ?

It would be great to permit to just pass all requests, independently from the method, to the same handler.

Use case

I have a service which for a specific Route:

  • Authenticate the request
  • Passthrough the request to an other service

The wildcard on the URL is super handy here because it permits to just write the base URL to the route (ex: /products/{any:*}) and call my handler doing the authentication and then pass-through. Therefore if I add new endpoints in my destination service, I won't need to update this one.

But since there is no equivalent for the methods, if I add endpoints with new methods in my destination service, I need to think to add equivalents in the authentication one. Making the maintainability harder and more error prone.

Thank you for considering this proposal and for this amazing router!

Failed to fetch package (hash sum mismatch)

#>go get github.com/fasthttp/router  
go get github.com/fasthttp/router
go: github.com/fasthttp/router upgrade => v1.1.3
go: downloading github.com/fasthttp/router v1.1.3
go get: github.com/fasthttp/[email protected]: verifying module: checksum mismatch
        downloaded: h1:q042AM9cSgVNvpG1T+lMAiL1PXLcTs94n1pCDQ737ss=
        sum.golang.org: h1:xxYAxTON68tX8pXwdw3cCPOYvKoXIbfrouBEuG6kFsk=

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.

Example code doesnt work with autocannon

Hi!
I'm trying to use autocannon to test the performance.
This is the configuration:
autocannon -c 10 -d 30 -m POST -b {"data":"teste"} http://localhost:3001/index

just changed GET to POST. But I get timeout for all the connections.
This is a bug??

ctx.userValues is shared across different request matches causing unpredictable results?

Hi there! :)

I've prepared a short snippet program localizing the issue:
go.mod:

module test

go 1.14

require (
	github.com/fasthttp/router v1.2.4
	github.com/valyala/fasthttp v1.15.1
)

main.go:

package main

import (
	httprouter "github.com/fasthttp/router"
	"github.com/valyala/fasthttp"
)

func main() {
	router := httprouter.New()

	var s string

	router.GET("/api/v1/path/special-path", func(ctx *fasthttp.RequestCtx) {
		println("from GET: " + s)
	})
	router.PATCH("/api/v1/path/{id}", func(ctx *fasthttp.RequestCtx) {
		s = ctx.UserValue("id").(string)
		println("from PATCH: " + s)
	})

	err := fasthttp.ListenAndServe("127.0.0.1:8080", router.Handler)
	if err != nil {
		panic(err)
	}
}

Then I make the following requests, one after another:
curl --location --request PATCH 'http://127.0.0.1:8080/api/v1/path/aaabbbcccdddzzz'
curl --location --request GET 'http://127.0.0.1:8080/api/v1/path/special-path'

The output I expect to see:

from PATCH: aaabbbcccdddzzz
from GET: aaabbbcccdddzzz

The actual output:

from PATCH: aaabbbcccdddzzz
from GET: special-pathzzz

As you can see the special-path path part overwrites part of the aaabbbcccdddzzz word.

Issue with catch-all parameter

Hello.
I currently try to serve a static web frontend under the /dashboard/* route:

router.GET("/", func(ctx *fasthttp.RequestCtx) {
    ctx.Redirect("/dashboard", fasthttp.StatusMovedPermanently)
})
router.GET("/dashboard/{filepath:*}", fasthttp.FSHandler("./web", 0))

However, it seems like the server includes the /dashboard/ prefix when it looks for local files:

2021/06/27 13:23:50 2.483 #0000000100000007 - 127.0.0.1:8080<->127.0.0.1:47280 - GET http://localhost:8080/dashboard/ - cannot open file "./web/dashboard": open ./web/dashboard: no such file or directory

Is there a way to prevent this?

Documentation for router.Lookup out of date

// Lookup allows the manual lookup of a method + path combo.

The documentation for the function router.Lookup mentions a third parameter and doesn't match the actual function body:

// Lookup allows the manual lookup of a method + path combo.
// This is e.g. useful to build a framework around this router.
// If the path was found, it returns the handler function and the path parameter
// values. Otherwise the third return value indicates whether a redirection to
// the same path with an extra / without the trailing slash should be performed.

I guess it should be:

// Lookup allows the manual lookup of a method + path combo.
// This is e.g. useful to build a framework around this router.
// If the path was found, it returns the handler function. Otherwise the second return value indicates whether a redirection to
// the same path with an extra / without the trailing slash should be performed.

It also doesn't metion what the ctx value is used for when looking up a handler and i can't really figure out why the ctx is needed here

request filter

Hello, may I ask, how can I implement the filter, before all requests, after the request add the filter, in the outer layer

Path Route

Hello,
I've been using the router for some time now, but I'm having trouble figuring out the path being called to include in the Datadog.
Below is an example of middleware, retrieves the path already with parameter value, but I would like to display it as the original path.

It is showing with the concatenated parameter and not as declared.

https://github.com/qgxpagamentos/ddfasthttp
``
func Middleware(f fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
if _, ok := os.LookupEnv("DATADOG_ENABLED"); !ok {
f(ctx)
return
}
spanOpts := []ddtrace.StartSpanOption{
tracer.ResourceName(string(ctx.Request.Header.Method()) + " " + string(ctx.Request.URI().Path())),
}
var r http.Request
if e := fasthttpadaptor.ConvertRequest(ctx, &r, true); e != nil {
panic(e)
}
span, context := StartRequestSpan(&r, spanOpts...)

	ctx.SetUserValue(DataDogTransaction, context)

	f(ctx)

	statusCode := ctx.Response.StatusCode()
	FinishRequestSpan(span, statusCode)
}

}
``
Screen Shot 2022-11-07 at 23 20 16
Screen Shot 2022-11-07 at 23 20 47

how to support path with and without trailing slash ?

I have a very restrictive requirement

I need to support path likes

/foo
/foo/

without redirect from one form to the other ( in other words, the last trailing slash should be ignored )

https://github.com/buaazp/fasthttprouter works fine for this, but fasthttp/router panics

panic: a handler is already registered for path '/stream/'

goroutine 1 [running]:
github.com/fasthttp/router/radix.(*Tree).Add(0xc0003ee640, {0xc000120540, 0x8}, 0xc0003ed110)

how can I achieve it?

Not removing superfluous path elements like ////

I use code from: https://github.com/fasthttp/router/tree/v1.3.1/_examples/basic
All in default.
Checking "fix wrong cases and remove superfluous path elements (like ../ or //)" because my currect web-server doesn't do it.

http://localhost:8080/ works, it is normal
http://localhost:8080///// works too, not expected
http://localhost:8080////hello/////name works, does not redirect to correct http://localhost:8080/hello/name
But if I use capital letters:
http://localhost:8080////hELLO/////name
it correctly redirects to http://localhost:8080/hello/name removing reduntant slashes.

Question: Why is MatchedRoutePathParam random?

MatchedRoutePathParam is used to store the original path registered by the user in ctx.UserValues. From the code, I found that this variable is defined with some random byte strings at runtime. What's the reasoning behind keeping this variable as random? Side effect of this is any downstream user of the library, including arbitrary middleware/wrappers need to now import this variable everywhere.

Additionally, do you think passing this variable from the user makes sense?

Thanks!

Mixing static + wildcard segment causes hung request

package main

import (
	"fmt"
	"log"

	"github.com/fasthttp/router"
	"github.com/valyala/fasthttp"
)

func Index(ctx *fasthttp.RequestCtx) {
	ctx.WriteString("Welcome!")
}

func Hello(ctx *fasthttp.RequestCtx) {
	fmt.Fprintf(ctx, "Hello, %s!\n", ctx.UserValue("name"))
}

func main() {
	r := router.New()
	r.GET("/hello/test", Index)
	r.GET("/hello/{name}", Hello)

	fmt.Println("starting")
	log.Fatal(fasthttp.ListenAndServe(":8080", r.Handler))
}

GET: /hello/test returns "Welcome!"
GET: /hello/test1 hangs indefinitely
GET: /hello/tes returns "Hello, tes!"

wildcard segment ':slug' conflicts with existing children in path '/:slug'

Same as buaazp/fasthttprouter#49.

I want use / path for render index page and /:slug for render specific content by it's :slug. I am aware about the hack through router.NotFound, but, firstly, this is not most obvious way, and secondly, it requires additional validations for the correct transitions between the necessary pages.

Example

func main() {
	...
	
	r := router.New()
	handler.RegisterAPI(r.Group("/api")) // api
	handler.RegisterTemplates(r) // renders
}

func (h *MyHandlers) RegisterTemplates(g *router.Router) {
	g.GET("/", h.RenderIndex)
	g.GET("/:slug", h.RenderSlug)
}


func (h *MyHandlers) RegisterAPI(g *router.Router) {
	g.GET("/", h.ShowAll)
	g.GET("/:slug", h.ShowOne)
}

Group wide middlewares

Would be nice if there was a way to specify middlewares for Group as otherwise currently you have to add route filtering in middlewares or add checks in all handlers

Method Specific Cache

I'm considering migration from fasthttprouter embedded version to fasthttp/router in Gramework. To do that, I need two features:

  • method-specific cache. you can see working implementation here.
  • Optional: an option for integration of custom contexts.

path been override

r := New()
v1 := r.Group("/v1")
v1.GET("/foo/{id}/{pageSize}/{page}", func(ctx *fasthttp.RequestCtx) {
	id1 = ctx.UserValue("id").(string)
	pageSize = ctx.UserValue("pageSize").(string)
	page = ctx.UserValue("page").(string)
	routed1 = true
})
v1.DELETE("/foo/{id}/{iid}", func(ctx *fasthttp.RequestCtx) {
	id2 = ctx.UserValue("id").(string)
	iid = ctx.UserValue("iid").(string)
	routed2 = true
})

the /foo/{id}/{iid} path will not been route.

panic: path must begin with '/' in path '' group.go:19

user := sm.Group("/user ")
user .GET("", h.UserHomePage)

error:
panic: path must begin with '/' in path ''

group.go

`func (g *Group) GET(path string, handler fasthttp.RequestHandler) {
validatePath(path)

g.router.GET(g.prefix+path, handler)

}`

Should it be changed to

`func (g *Group) GET(path string, handler fasthttp.RequestHandler) {
validatePath(g.prefix+path)

g.router.GET(g.prefix+path, handler)

}`

Issue with trailing slashes

The following code does not handle trailing slashes as expected. The docs say that requests with a trailing slash will be handled the same as without a trailing slash. This does not work in the below code;

package main

import (
	"fmt"
	"github.com/fasthttp/router"
	"github.com/valyala/fasthttp"
	"log"
	"time"
)

var (
	s = &fasthttp.Server{
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  120 * time.Second,
		GetOnly:      true,
	}
)

func greet(ctx *fasthttp.RequestCtx) {
	ctx.WriteString(fmt.Sprintf("Hi %s\n", ctx.UserValue("name").(string)))
}

func main() {
	r := router.New()
	r.RedirectTrailingSlash = true
	r.GET("/:name", greet)
	s.Handler = r.Handler
	log.Fatal(s.ListenAndServe(":8080"))
}

Testing this gives;

jonathan@ubuntu:~$ curl 127.0.0.1:8080/joe
Hi joe
jonathan@ubuntu:~$ curl 127.0.0.1:8080/joe/
jonathan@ubuntu:~$

If there is a trailing slash the handler is not executed. How would I fix this?

Panic with 1.0.0 when using unspecified method and multiple params

This started with 1.0.0.

Error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x69823b]

goroutine 20 [running]:
github.com/valyala/fasthttp.(*RequestCtx).SetUserValue(...)
	/home/greg/go/pkg/mod/github.com/valyala/[email protected]/server.go:581
github.com/fasthttp/router/radix.(*node).getFromChild(0xc0000b45a0, 0xc00001e1d6, 0xa, 0x0, 0x10, 0x9592bc)
	/home/greg/go/pkg/mod/github.com/fasthttp/[email protected]/radix/node.go:307 +0x59b
github.com/fasthttp/router/radix.(*node).getFromChild(0xc0000b4420, 0xc00001e1d1, 0xf, 0x0, 0x410404, 0xc000057a10)
	/home/greg/go/pkg/mod/github.com/fasthttp/[email protected]/radix/node.go:302 +0x4b1
github.com/fasthttp/router/radix.(*Tree).Get(0xc0000b2040, 0xc00001e1d0, 0x10, 0x0, 0xc00009d678, 0x10)
	/home/greg/go/pkg/mod/github.com/fasthttp/[email protected]/radix/tree.go:88 +0x1f7
github.com/fasthttp/router.(*Router).allowed(0xc0000b4360, 0xc00001e1d0, 0x10, 0xc00001e138, 0x4, 0x0, 0x3200000000000000)
	/home/greg/go/pkg/mod/github.com/fasthttp/[email protected]/router.go:405 +0x5dc
github.com/fasthttp/router.(*Router).Handler(0xc0000b4360, 0xc000078000)
	/home/greg/go/pkg/mod/github.com/fasthttp/[email protected]/router.go:511 +0x37a
github.com/valyala/fasthttp.(*Server).serveConn(0xc0000fc000, 0x7a4f40, 0xc0000b2088, 0x0, 0x0)
	/home/greg/go/pkg/mod/github.com/valyala/[email protected]/server.go:2041 +0x554
github.com/valyala/fasthttp.(*workerPool).workerFunc(0xc0000beb40, 0xc0000a6460)
	/home/greg/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:223 +0xc0
github.com/valyala/fasthttp.(*workerPool).getCh.func1(0xc0000beb40, 0xc0000a6460, 0x6bb4a0, 0xc0000a6460)
	/home/greg/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:195 +0x35
created by github.com/valyala/fasthttp.(*workerPool).getCh
	/home/greg/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:194 +0x101

Code:

package main

import (
	"log"

	"github.com/fasthttp/router"
	"github.com/valyala/fasthttp"
)

func main() {
	r := router.New()
	r.GET("/{version}/{service}/{value}", func(ctx *fasthttp.RequestCtx) {})

	log.Fatal(fasthttp.ListenAndServe(":8080", r.Handler))
}

Request:

curl  -d '{}' 'localhost:8080/does/not/matter'

CORS allow ?

Like Gin Cors()
Or:

ctx.Response.Header.Set("Access-Control-Allow-Origin", "*")

Disallow dot in path

Hello, I have a route
r.GET("/file/:file.json", routes.GetFile)
and request GET localhost:8080/file/somefile.json
I am expecting ctx.UserValue("file") -> "somefile"
but received ctx.UserValue("file.json") -> somefile.json

How to correctly process this behaviour, without any additional trimming and etc?

Need a way to write unit test with url parameters [SOLVED]

hello, I want to ask about how to do unit testing when I have url parameters(e.g /:name) on my url ?
because at this fasthttp they uses requestCtx, I am a little confused to do unit tests when I have url parameters.

Thanks before,

incorrect regex match

if i have the following routes:

"/regex/{c1:big_alt|alt|small_alt}/{rest:*}"
and
/regex/{path:*}

the following path will match with the first route:

"/regex/small_alt/hello"

Middleware for Group

Hi, in my server i have routes for mobile app and site.
I split routes by Group
Can I get route.Group with middleware ?
Something Like that:

r := router.New()
mobileRoutes := r.Group("/api/v1/mibile")
//...
mobileRoutes = middleware.CheckJWT(mobileRoutes)

siteRoutes := r.Group("/api/v1/site")
//...
siteRoutes = middleware.Cors(siteRoutes )

optional parameter get request

Hello,

Is this possible to define optional parameter like

/foo/:barvalue/?:baroption=1 // < baroption is optional we can say /foo/value and /foo/value/?option=1

What is the best way to achieve that with the router ?

Translate something like that with the framework ?
GET /foo?option=value&bar=otheroption 

go mod install package time out???

fasttools ➤ go build
go: downloading github.com/fasthttp/router v0.5.1
go: downloading github.com/valyala/fasthttp v1.5.0
verifying github.com/valyala/[email protected]: github.com/valyala/[email protected]: Get https://sum.golang.org/lookup/github.com/valyala/[email protected]: dial tcp 172.217.27.145:443: i/o timeout
verifying github.com/fasthttp/[email protected]: github.com/fasthttp/[email protected]: Get https://sum.golang.org/lookup/github.com/fasthttp/[email protected]: dial tcp 172.217.27.145:443: i/o timeout

405 no longer returned on unsupported method when using named parameters

It seems this broke in 1.1.x. For example, using the "trivial example" from the README.md:

$ curl -v -d {} http://localhost:8080/hello/fdas
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /hello/fdas HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 2
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 2 out of 2 bytes
< HTTP/1.1 404 Not Found
< Server: fasthttp
< Date: Mon, 01 Jun 2020 17:33:52 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 9
< 
* Connection #0 to host localhost left intact
Not Found

Whereas with older version:

$ curl -v -d {} http://localhost:8080/hello/fdas
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /hello/fdas HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 2
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 2 out of 2 bytes
< HTTP/1.1 405 Method Not Allowed
< Server: fasthttp
< Date: Mon, 01 Jun 2020 17:38:19 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 18
< Allow: GET, OPTIONS
< 
* Connection #0 to host localhost left intact
Method Not Allowed

How to update or remove a route?

Hi, I see there is a trees.Add() when adding a route. If I want to delete a route or update the handler of an existing route, is there API for manipulating the "trees"?

Routing not working properly with catch-all params

I have some problems routing some requests when using catch-all params. I am using version v1.1.1 and this is the code:

package main

import (
	"log"

	"github.com/fasthttp/router"
	"github.com/valyala/fasthttp"
)

func Catchall(ctx *fasthttp.RequestCtx) {
	ctx.WriteString("Catchall!")
}

func Specific(ctx *fasthttp.RequestCtx) {
	ctx.WriteString("Specific!")
}

func main() {
	r := router.New()
	r.ANY("/{path:*}", Catchall)
	r.POST("/specific", Specific)

	log.Fatal(fasthttp.ListenAndServe(":8080", r.Handler))
}

I would expect any request different than POST /specific to be caught by the Catchall handler, but this is not what is actually happening. For the exact POST /specific and any request that is not starting with /specific it works fine but for the following cases it is not:

  • GET /specific
  • GET /specific/whatever
  • POST /specific/whatever

The server is just returning a redirect. I think this is due to the radix tree, but I would expect another behavior.

Why this routing doesn't work?

router.GET("/hello", helloHandlerFunc)
router.GET("/{param}", paramHandler)

When I try to get /hellowhatever, or even any that start with "h" like /hey, it returns ERR_TOO_MANY_REDIRECTS.

Why is this happening?

Get Matched Route

How can i get the matched route in my handle?

Example:

Assuming that i've registered the route

r.GET("/hello/{name}", Hello)

In Hello handle i would like to get "/hello/{name}" path

Is it possible?

Behavior change in 1.3.2 with double slashes when RedirectFixedPath is false

1.3.2 changed the behavior of RedirectFixedPath. Before 1.3.2, double slashes would be ignored when RedirectFixedPath was set to false. As of 1.3.2, a 404 is now returned.

Modifying the example in the README slightly:

package main

import (
	"fmt"
	"log"

	"github.com/fasthttp/router"
	"github.com/valyala/fasthttp"
)

func Index(ctx *fasthttp.RequestCtx) {
	ctx.WriteString("Welcome!")
}

func Hello(ctx *fasthttp.RequestCtx) {
	fmt.Fprintf(ctx, "Hello, %s!\n", ctx.UserValue("name"))
}

func main() {
	r := router.New()

	r.RedirectFixedPath = false

	r.GET("/", Index)
	r.GET("/hello/{name}", Hello)

	log.Fatal(fasthttp.ListenAndServe(":8080", r.Handler))
}

On 1.3.1:

$ curl -v http://localhost:8080/hello//fdasf
*   Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /hello//fdasf HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: fasthttp
< Date: Thu, 10 Sep 2020 18:17:30 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 14
< 
Hello, fdasf!
* Connection #0 to host localhost left intact

On 1.3.2:

$ curl -v http://localhost:8080/hello//fdasf
*   Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /hello//fdasf HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Server: fasthttp
< Date: Thu, 10 Sep 2020 18:17:53 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 9
< 
* Connection #0 to host localhost left intact

Consultation on route matching

Will there be this routing mode in the future?
Due to historical reasons, we have a problem as follows:
/xxx/xxxx/aaa
/xxx/xxxx/aaa/
Two routes (can be registered), when RedirectTrailingSlash is false, these two can be matched
In fasthttp, only one of these two can be registered. When registering /xxx/xxxx/aaa/, when routing /xxx/xxxx/aaa, you can redirect. I want to be able to match。

I temporarily modified the code and it works normally, but I don’t know if there are hidden dangers. Can you reply to the next and support me so that I can update in time, or tell if there are any hidden dangers?

Code:
types.go

type Router struct {
...
    //When redirecting, it will be combined according to the path of the child to re matchFor example
    //For example:/xxx/xxx/aaa => /xxx/xxx/aaa/ or /xxx/xxx/aaa/ => /xxx/xxx/aaa 
    NoRedirectWithRootPath bool
...
}

router.go line:341

func (r *Router) tryRedirect(ctx *fasthttp.RequestCtx, tree *radix.Tree, tsr bool, method, path string) bool {
...
    if tsr && r.RedirectTrailingSlash {
		uri := bytebufferpool.Get()

		getRedirectUri(ctx,path,uri)
		ctx.Redirect(uri.String(), code)

		bytebufferpool.Put(uri)

		return true
	}

	if tsr && r.NoRedirectWithRootPath {
		uri := bytebufferpool.Get()

		getRedirectUri(ctx,path,uri)
		if uri.Len() > 0 {
			if parHandler ,_:= tree.Get(uri.String(), ctx); parHandler != nil {
				parHandler(ctx)
			}
		}
		bytebufferpool.Put(uri)
		return true
	}
...
}

func getRedirectUri(ctx *fasthttp.RequestCtx,path string,uri *bytebufferpool.ByteBuffer) {
	if len(path) > 1 && path[len(path)-1] == '/' {
		uri.SetString(path[:len(path)-1])
	} else {
		uri.SetString(path)
		uri.WriteString("/")
	}

	queryBuf := ctx.URI().QueryString()
	if len(queryBuf) > 0 {
		uri.WriteByte(questionMark)
		uri.Write(queryBuf)
	}
}

how to serve files using embed?

i had this code in 'net/http' and we were serving static directory using this code
but now we have shifted our code(projects) from net/http to fasthttp
and in fasthttp there is no way to serve files from embed

//go:embed static/*
var static embed.FS
fsRoot, _ := fs.Sub(static, "static")
fsStatic := http.FileServer(http.FS(fsRoot))
s.router.PathPrefix("/").Handler(fsStatic)

path validation breaks group usage

New feature "path validation" breaks my group usage

I have router structure like:

apiMonitors := router.Group("/monitors")
apiMonitors.GET("", h...)
apiMonitors.POST("", h...)
apiMonitors.GET("/{oid}", h...)
apiMonitors.PUT("/{oid}", h...)
apiMonitors.DELETE("/{oid}", h...)
apiMonitors.GET("/{jobId}/run", h...)
apiMonitors.GET("/{jobId}/complete", h...)
apiMonitors.GET("/{jobId}/fail", h...)
apiMonitors.GET("/{jobId}/pause", h...)

and now I get error panic: path must begin with '/' in path ''

Will this be fixed or do I need to rebuild my structure?

ServeFilesCustom calls PanicHandler if file does not exist

My router for domain http://st.domain.test

func StaticRouter(r *router.Router) {
	r.NotFound = NotFound
	r.MethodNotAllowed = MethodNotAllowed
	r.PanicHandler = InternalServerError

	r.ServeFilesCustom("/{filepath:*}", &fasthttp.FS{
		Root:          "./static",
		PathNotFound:  NotFound,
		CacheDuration: time.Hour * 24 * 30,
	})
}

For not existing request, for example, http://st.domain.test:8090/abrakadabra, router write to console:
2020/12/17 10:11:07 0.960 #0000000800000003 - 127.0.0.1:8090<->127.0.0.1:50255 - GET http://st.domain.test:8090/abrakadabra - cannot open file "./static/abrakadabra": open ./static/abrakadabra: The system cannot find the file specified.
And calls handler for PanicHandler. I expect "NotFound" in this situation.

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.