Previously, the `NotFound` status code was used to signal many kinds of
recoverable, forwarding errors. This included validation errors, incorrect
Content-Type errors, and more.
This commit modifies the status code used to forward in these instances to more
precisely indicate the forwarding condition. In particular:
* Parameter `FromParam` errors now forward as 422 (`UnprocessableEntity`).
* Query paramater errors now forward as 422 (`UnprocessableEntity`).
* Use of incorrect form content-type forwards as 413 (`UnsupportedMediaType`).
* `WebSocket` guard now forwards as 400 (`BadRequest`).
* `&Host`, `&Accept`, `&ContentType`, `IpAddr`, and `SocketAddr` all forward
with a 500 (`InternalServerError`).
Additionally, the `IntoOutcome` trait was overhauled to support functionality
previously offered by methods on `Outcome`. The `Outcome::forward()` method now
requires a status code to use for the forwarding outcome.
Finally, logging of `Outcome`s now includes the relevant status code.
Resolves#2626.
The primary motivation is to deconflate the leading `F`s in `Failure` and
`Forward`. In particular, when using a generics, we used `F` for forward, which
could easily be confused for `F` for `Failure`. This resolves the conflation.
The warning is fairly conservative. Heuristics are used to determine if a call
to `tokio::spawn()` occurs in the `#[launch]` function.
Addresses #2547.
Previously, `async_main` would extract a full `Config`. This mean that values
like `address` were read and parsed even when they were unused. Should they
exist and be malformed, a configuration error would needlessly arise.
This commit fixes this by only extract values that are subsequently used.
The codegen for field validations previously included a closure that
could potentially partially borrow a 'Copy' field of the context
structure. To prevent this, 'let'-assign the field before the closure is
created, and use the assignment inside of the closure.
The compatibility normalizer previously missed or was overly egregious
in several cases. This commit resolves those issue. In particular:
* Only request URIs that would not match any route are normalized.
* Synthetic routes are added to the igniting `Rocket` so that requests
with URIs of the form `/foo` match routes with URIs of the form
`/foo/<b..>`, as they did prior to the trailing slash overhaul.
Tests are added for all of these cases.
Prior to this commit, a route with a URI of `/` could not be mounted in
such a way that the resulting effective URI contained a trailing slash.
This commit changes the semantics of mounting so that mounting such a
route to a mount point with a trailing slash yields an effective URI
with a trailing slash. When mounted to points without a trailing slash,
the effective URI does not have a trailing slash.
This commit also introduces the `Route::rebase()` and
`Catcher::rebase()` methods for easier rebasing of existing routes and
catchers.
Finally, this commit improves logging such that mount points of `/`
are underlined in the logs.
Tests and docs were added and modified as necessary.
Resolves#2533.
If a port part was missing, the 'Authority' parser previously set the
port to `0`. This is incorrect. As in RFC#3986 3.2.3:
> URI producers and normalizers should omit the port component and its
":" delimiter if port is empty [..]
This commit fixes the parser's behavior to align with the RFC.
'EXE' is IANA registered, and the registered media type is used here for
the '.exe' extension.
The '.iso' and '.dmg' extensions do not appear to correspond to any IANA
registered media type, but they have a de facto media type of
"application/octet-stream", and that media type is used by this commit.
Closes#2530.
Prior to this commit, all forward outcomes resulted in a 404. This
commit changes request and data guards so that they are able to provide
a `Status` on `Forward` outcomes. The router uses this status, if the
final outcome is to forward, to identify the catcher to invoke.
The net effect is that guards can now customize the status code of a
forward and thus the error catcher invoked if the final outcome of a
request is to forward.
Resolves#1560.
This commit exposes four new methods:
* `Route::collides_with(&Route)`
* `Route::matches(&Request)`
* `Catcher::collides_with(&Catcher)`
* `Catcher::matches(Status, &Request)`
Each method checks the corresponding condition: whether two routes
collide, whether a route matches a request, whether two catchers
collide, and whether a catcher matches an error arising from a request.
This functionality is used internally by Rocket to make routing
decisions. By exposing these methods, external libraries can use
guaranteed consistent logic to check the same routing conditions.
Resolves#1561.
Prior to this commit, several `RouteUri` fields were public, allowing
those values to be changed at will. These changes were at times not
reflected by the rest of the library, meaning that the values in the
route URI structure for a route became incoherent with the reflected
values. This commit makes all fields private, forcing all changes to go
through methods that can ensure coherence. All values remain accessible
via getter methods.
This commit modifies request routing in a backwards incompatible manner.
The change is summarized as: trailing slashes are now significant and
never transparently disregarded. This has the following implications,
all representing behavior that differs from that before this change:
* Route URIs with trailing slashes (`/foo/`, `/<a>/`) are legal.
* A request `/foo/` is routed to route `/foo/` but not `/foo`.
* Similarly, a request `/bar/` is routed to `/<a>/` but not `/<a>`.
* A request `/bar/foo` is not routed to `/<a>/<b>/<c..>`.
A new `AdHoc::uri_normalizer()` fairing was added that recovers the
previous behavior.
In addition to the above, the `Options::NormalizeDirs` `FileServer`
option is now enabled by default to remain consistent with the above
changes and reduce breaking changes at the `FileServer` level.
The fuzzing target introduced in this commit attemps to assert
"collision safety". Formally, this is the property that:
matches(request, route) := request is matched to route
collides(route1, route2) := there is a a collision between routes
forall requests req. !exist routes r1, r2 s.t.
matches(req, r1) AND matches(req, r2) AND not collides(r1, r2)
Alternatively:
forall requests req, routes r1, r2.
matches(req, r1) AND matches(req, r2) => collides(r1, r2)
The target was run for 20 CPU hours without failure.