-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Echo Does Not Correctly Handle URLs with a RawPath
#1974
Comments
What does that I'm trying that same case with curl and this request will not get even past http server code. func main() {
e := echo.New()
e.GET("/foo/bar/:msg", func(c echo.Context) error {
return c.String(http.StatusOK, c.Param("msg"))
})
if err := e.Start(":8088"); err != http.ErrServerClosed {
log.Fatal(err)
}
}
|
seems so. But I do not seems to get x@x:~/code$ curl -v "http://localhost:8088/foo/bar/what's%20up"
* Trying 127.0.0.1:8088...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8088 (#0)
> GET /foo/bar/what's%20up HTTP/1.1
> Host: localhost:8088
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
< Date: Thu, 26 Aug 2021 15:16:33 GMT
< Content-Length: 11
<
* Connection #0 to host localhost left intact
what's%20up Is your problem This should be cleared as a first thing. Before we move to solutions we should all understand in a super simple example what is needs to happen "when given input X then output should be Y" There are historical issues where some need escaping and issued where it is not wanted. Some even unescape path params in middleware, some in handlers. For proxy mw users path param escaping/unescaping is imprtant. p.s. we are planning to introduce flag for router in p.s.s. sorry if this is said in first post but it is way easier to start with case you can debug than try to backtrack quite large text of solution/related info. |
Maybe using URL to build your request |
Here is an example of Go code: package main
import (
"net/http"
"github.com/labstack/echo/v4"
)
func main() {
// Echo instance
e := echo.New()
// Routes
e.GET("/", hello)
e.GET("/foo/bar/what's up", hello)
e.GET("foo/param/:name", handleParam)
// Start server
e.Logger.Fatal(e.Start(":1323"))
}
// Handler
func hello(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
}
func handleParam(c echo.Context) error {
return c.String(http.StatusOK, c.Param("name"))
} Then, in Firefox, I navigate to fetch("http://localhost:1323/foo/bar/what's up")
fetch("http://localhost:1323/foo/param/what's up")
As I say in the description:
So yes it is easy to adapt the JS app so that the bug does not trigger anymore, but Echo should still be capable of handling requests with unescaped single quotes to be compliant with the URI standard. |
Note to self:
This is one of those hard problems where change (correct or not) could break expectations that people have built around routing and path params. Going through history it seems that using rawPath for routing is feature that Echo has had very long time. Some related issues: Things to consider when changing:
Expectations:
|
Hi, I'm facing this issue in my application. Proposals:
|
Hi @ikedam
This will be introduced in
This is where path is decided for routing purposes. Note Now in serving static files with It would be very helpful if you could paste short example here with |
Thanks! I watch that ticket and wait for So My case is illustrated here: https://go.dev/play/p/Or-RPfI6me5 |
Issue Description
Abstract:
func (r *Router) Find(method, path string, c Context)
(in filerouter.go
) is sometimes given a raw path instead of a unescaped path, but because it has no way to tell between the two situations it does not behave properly if given a raw path.Argument
path
offunc (r *Router) Find
is provided byfunc GetPath(r *http.Request) string
(in fileecho.go
) which returns thePath
of the request's URL, unless the URL structure has aRawPath
in which case it returns theRawPath
. A URL structure from packagenet/url
has aRawPath
ifescape(unescape(RawPath)) != RawPath
(whereescape
applies URL percent-encoding). Seenet/url
source code. The rationale of this extraRawPath
field innet/url
package is to handle the case of URLs containing%2F
, whichunescape
will transform into character/
but which should not be used as a path separator by the server.What a server should do when it receives an URL which has a
RawPath
is:RawPath
to split the path into segments based on the ”true”/
characters (the ones not coming from unescaping%2F
)Because Echo's
func (r *Router) Find
does not when the path it received is a RawPath, it fails to do step (2), and this causes bugs.Checklist
Expected behaviour vs. Actual behaviour (+ steps to reproduce)
If a JavaScript application sends a request with a path containing and single quote and a space, such as
fetch("/foo/bar/what's up")
, Echo will return aHTTP 404 Not Found
even if there was a handler for"/foo/bar/what's up"
or"/foo/bar/:msg"
. The reasons are that:net/url
does escape the single quote (see net/url: URL.String() hashbang "#!" encodes as "#%21" golang/go#19917 and golang/go@8a330454)escape(unescape(RawPath))
ends withwhat%27s%20up
whileRawPath
ends withwhat's%20up
, so the two don't match and URL's RawPath is set.func GetPath(r *http.Request) string
will then pass the raw URL toFind
, ending withwhat's%20up
, and sinceFind
never does any unescaping, weird behaviors will appear: a handler for"/foo/bar/what's up"
will not match, or path parametermsg
will be set towhat's%20up
instead ofwhat's up
.Of course, this situation can be fixed easily by modifying the JS application to encode the single quote. But strictly speaking it's not the fault of the JS app: it is legal not to escape the single quote, and the server (that is, Echo) should work with unescaped path fragments even if it had to do path splitting on a raw path.
Suggested Solutions
One way would be to change
Find
's argumentpath string
topathSegments []string
, where the caller ofFind
would be responsible for splitting the path into segments. This way if the URL has aRawPath
the caller can use it to do the splitting, then unescape all parts before sending them toFind
.The problem is that
Find
currently processespath
byte-by-byte, and it would probably require a complete rewrite ofFind
(and potentially other parts of Echo, such as the logic adding a handler to a router) to operate on path segments.I don't see any solution that would correctly handle URLs with a
RawPath
and would not require a big rewrite. A few mitigations I can suggest:add an option to skip
func GetPath(r *http.Request) string
and always use the URL'sPath
insteadGive
Find
a way to tell when it got a raw path (raw bool
argument?) and then replacingparamValues[paramIndex] = search[:i]
with:This will at least fix the situation for path parameters. It will still break if path segments contains percent-encoded characters.
add a warning about this known issue somewhere visible
Version/commit
7f502b1
The text was updated successfully, but these errors were encountered: