-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Uri
issues, summarized
#998
Comments
A lot of places get this wrong. I made a comment when Request Target, e.g. what appears in
( It is not a superset of For
and
Depending on how far you want to go with static safety, the types will multiply. |
The OAuth v2.0 implicit grant can return the access token and other key value pairs in the fragment of the URI. The fragment is appended to the redirect URI and the user is redirected after logging in. However, URI fragment parsing is typically done from the user agent or client side. What is a more valid use case is that a server may need to send a redirect using the location response header field with a URI that includes a fragment. The OAuth spec notes that some servers do not support fragments in the URI and that a workaround could be to return an HTML page with a button linked to the redirect URI. I am not advocating for adding URI fragment support necessarily but I do believe there are some valid use cases like this. |
FWIW, the issues with |
I'm attempting to integrate a web service with AWS Cognito, a hosted authn/authz provider. I redirect my clients to Cognito, the client logs in, then Cognito redirects the client with a JSON Web Token to me with the format
As you can see, Cognito uses the I'm posting this comment so there is more data on the various use cases that the current URI implementation does not support. Thanks! Cognito documentation: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-integration.html (scroll near bottom) UPDATEIt appears that my understanding of fragments was misguided, and I should access the data from the fragment another way, perhaps through javascript. |
Here's another issue; moving it here from #827 (comment):
None of the commented-out This was discussed in IRC (https://mozilla.logbot.info/rocket/20190816) but I don't remember coming to any conclusion. |
I hope this is the correct place to add these thoughts. I can open a separate issue if that would be more helpful. As discussed in #1021 (and probably elsewhere), it's relatively common for server responses to need to include full URIs to its own endpoints. While the current I'm entirely new to rocket, so this initial spitballing is likely to be off-base, so I don't want to suggest a solution which will likely be very wrong. If it is decided that the current set-up is actually ideal and the problem is fundamentally a bit messy, then perhaps providing an example/docs for how to typically do this would be a good substitute. |
Routing: * Unicode characters are accepted anywhere in route paths. (#998) * Dyanmic query values can (and must) be any `FromForm` type. The `Form` type is no longer useable in any query parameter type. Capped * A new `Capped` type is used to indicate when data has been truncated due to incoming data limits. It allows checking whether data is complete or truncated. `DataStream` methods returns `Capped` types. * Several `Capped<T>` types implement `FromData`, `FromForm`. * HTTP 413 (Payload Too Large) errors are now returned when the data limit is exceeded. (resolves #972) Hierarchical Limits * Data limits are now hierarchical, delimited with `/`. A limit of `a/b/c` falls back to `a/b` then `a` when not set. Temporary Files * A new `TempFile` data and form guard allows streaming data directly to a file which can then be persisted. * A new `temp_dir` config parameter specifies where to store `TempFile`. * The limits `file` and `file/$ext`, where `$ext` is the file extension, determines the data limit for a `TempFile`. Forms Revamp * All form related types now reside in a new `form` module. * Multipart forms are supported. (resolves #106) * Collections are supported in body forms and queries. (resolves #205) * Nested forms and structures are supported. (resolves #313) * Form fields can be ad-hoc validated with `#[field(value = expr)]`. Core: * `&RawStr` no longer implements `FromParam`. * `&str` implements `FromParam`, `FromData`, `FromForm`. * `FromTransformedData` was removed. * `FromData` gained a lifetime for use with request-local data. * All dynamic paramters in a query string must typecheck as `FromForm`. * `FromFormValue` removed in favor of `FromFormField`. * Dyanmic paramters, form values are always percent-decoded. * The default error HTML is more compact. * `&Config` is a request guard. * The `DataStream` interface was entirely revamped. * `State` is only exported via `rocket::State`. * A `request::local_cache!()` macro was added for storing values in request-local cache without consideration for type uniqueness by using a locally generated anonymous type. * `Request::get_param()` is now `Request::param()`. * `Request::get_segments()` is now `Request::segments()`, takes a range. * `Request::get_query_value()` is now `Request::query_value()`, can parse any `FromForm` including sequences. * `std::io::Error` implements `Responder` as `Debug<std::io::Error>`. * `(Status, R)` where `R: Responder` implements `Responder` by setting overriding the `Status` of `R`. * The name of a route is printed first during route matching. * `FlashMessage` now only has one lifetime generic. HTTP: * `RawStr` implements `serde::{Serialize, Deserialize}`. * `RawStr` implements _many_ more methods, in particular, those related to the `Pattern` API. * `RawStr::from_str()` is now `RawStr::new()`. * `RawStr::url_decode()` and `RawStr::url_decode_lossy()` only allocate as necessary, return `Cow`. * `(Status, R)` where `R: Responder` is a responder that overwrites the status of `R` to `Status`. * `Status` implements `Default` with `Status::Ok`. * `Status` implements `PartialEq`, `Eq`, `Hash`, `PartialOrd`, `Ord`. * Authority and origin part of `Absolute` can be modified with new `Absolute::{with,set}_authority()`, `Absolute::{with,set}_origin()` methods. * `Origin::segments()` was removed in favor of methods split into query and path parts and into raw and decoded parts. * The `Segments` iterator is signficantly smarter. Returns `&str`. * `Segments::into_path_buf()` is now `Segments::to_path_buf()`, doesn't consume. * A new `QuerySegments` is the analogous query segment iterator. * Once set, the `expires` field on private cookies is not overwritten. (resolves #1506) * `Origin::path()` and `Origin::query()` return `&RawStr`, not `&str`. Codegen: * Preserve more spans in `uri!` macro. * `FromFormValue` derive removed; `FromFormField` added. * The `form` `FromForm` and `FromFormField` field attribute is now named `field`. `#[form(field = ..)]` is now `#[form(name = ..)]`. Examples: * `form_validation` and `form_kitchen_sink` removed in favor of `forms` * `rocket_contrib::Json` implements `FromForm`. * The `json!` macro is exported as `rocket_contrib::json::json`. * `rocket_contrib::MsgPack` implements `FromForm`. * Added clarifying docs to `StaticFiles`. * The `hello_world` example uses unicode in paths. Internal: * Codegen uses new `exports` module with the following conventions: - Locals starts with `__` and are lowercased. - Rocket modules start with `_` are are lowercased. - Stdlib types start with `_` are are titlecased. - Rocket types are titlecased. * A `header` module was added to `http`, contains header types. * `SAFETY` is used as doc-string keyword for `unsafe` related comments. * The `Uri` parser no longer recognizes Rocket route URIs.
Routing: * All UTF-8 characters are accepted anywhere in route paths. (#998) * `path` is now `uri` in `route` attribute: `#[route(GET, path = "..")]` becomes `#[route(GET, uri = "..")]`. Forms Revamp * All form related types now reside in a new `form` module. * Multipart forms are supported. (resolves #106) * Collections are supported in body forms and queries. (resolves #205) * Nested forms and structures are supported. (resolves #313) * Form fields can be ad-hoc validated with `#[field(value = expr)]`. * `FromFormValue` is now `FromFormField`, blanket implements `FromForm`. * Form field values are always percent-decoded apriori. Temporary Files * A new `TempFile` data and form guard allows streaming data directly to a file which can then be persisted. * A new `temp_dir` config parameter specifies where to store `TempFile`. * The limits `file` and `file/$ext`, where `$ext` is the file extension, determines the data limit for a `TempFile`. Capped * A new `Capped` type is used to indicate when data has been truncated due to incoming data limits. It allows checking whether data is complete or truncated. `DataStream` methods return `Capped` types. * Several `Capped<T>` types implement `FromData`, `FromForm`. * HTTP 413 (Payload Too Large) errors are now returned when data limits are exceeded. (resolves #972) Hierarchical Limits * Data limits are now hierarchical, delimited with `/`. A limit of `a/b/c` falls back to `a/b` then `a`. Core * `&RawStr` no longer implements `FromParam`. * `&str` implements `FromParam`, `FromData`, `FromForm`. * `FromTransformedData` was removed. * `FromData` gained a lifetime for use with request-local data. * The default error HTML is more compact. * `&Config` is a request guard. * The `DataStream` interface was entirely revamped. * `State` is only exported via `rocket::State`. * A `request::local_cache!()` macro was added for storing values in request-local cache without consideration for type uniqueness by using a locally generated anonymous type. * `Request::get_param()` is now `Request::param()`. * `Request::get_segments()` is now `Request::segments()`, takes a range. * `Request::get_query_value()` is now `Request::query_value()`, can parse any `FromForm` including sequences. * `std::io::Error` implements `Responder` as `Debug<std::io::Error>`. * `(Status, R)` where `R: Responder` implements `Responder` by overriding the `Status` of `R`. * The name of a route is printed first during route matching. * `FlashMessage` now only has one lifetime generic. HTTP * `RawStr` implements `serde::{Serialize, Deserialize}`. * `RawStr` implements _many_ more methods, in particular, those related to the `Pattern` API. * `RawStr::from_str()` is now `RawStr::new()`. * `RawStr::url_decode()` and `RawStr::url_decode_lossy()` only allocate as necessary, return `Cow`. * `Status` implements `Default` with `Status::Ok`. * `Status` implements `PartialEq`, `Eq`, `Hash`, `PartialOrd`, `Ord`. * Authority and origin part of `Absolute` can be modified with new `Absolute::{with,set}_authority()`, `Absolute::{with,set}_origin()` methods. * `Origin::segments()` was removed in favor of methods split into query and path parts and into raw and decoded versions. * The `Segments` iterator is smarter, returns decoded `&str` items. * `Segments::into_path_buf()` is now `Segments::to_path_buf()`. * A new `QuerySegments` is the analogous query segment iterator. * Once set, `expires` on private cookies is not overwritten. (resolves #1506) * `Origin::path()` and `Origin::query()` return `&RawStr`, not `&str`. Codegen * Preserve more spans in `uri!` macro. * Preserve spans `FromForm` field types. * All dynamic parameters in a query string must typecheck as `FromForm`. * `FromFormValue` derive removed; `FromFormField` added. * The `form` `FromForm` and `FromFormField` field attribute is now named `field`. `#[form(field = ..)]` is now `#[field(name = ..)]`. Contrib * `Json` implements `FromForm`. * `MsgPack` implements `FromForm`. * The `json!` macro is exported as `rocket_contrib::json::json!`. * Added clarifying docs to `StaticFiles`. Examples * `form_validation` and `form_kitchen_sink` removed in favor of `forms`. * The `hello_world` example uses unicode in paths. * The `json` example only allocates as necessary. Internal * Codegen uses new `exports` module with the following conventions: - Locals starts with `__` and are lowercased. - Rocket modules start with `_` and are lowercased. - `std` types start with `_` and are titlecased. - Rocket types are titlecased. * A `header` module was added to `http`, contains header types. * `SAFETY` is used as doc-string keyword for `unsafe` related comments. * The `Uri` parser no longer recognizes Rocket route URIs.
Routing: * All UTF-8 characters are accepted anywhere in route paths. (#998) * `path` is now `uri` in `route` attribute: `#[route(GET, path = "..")]` becomes `#[route(GET, uri = "..")]`. Forms Revamp * All form related types now reside in a new `form` module. * Multipart forms are supported. (resolves #106) * Collections are supported in body forms and queries. (resolves #205) * Nested forms and structures are supported. (resolves #313) * Form fields can be ad-hoc validated with `#[field(value = expr)]`. * `FromFormValue` is now `FromFormField`, blanket implements `FromForm`. * Form field values are always percent-decoded apriori. Temporary Files * A new `TempFile` data and form guard allows streaming data directly to a file which can then be persisted. * A new `temp_dir` config parameter specifies where to store `TempFile`. * The limits `file` and `file/$ext`, where `$ext` is the file extension, determines the data limit for a `TempFile`. Capped * A new `Capped` type is used to indicate when data has been truncated due to incoming data limits. It allows checking whether data is complete or truncated. `DataStream` methods return `Capped` types. * Several `Capped<T>` types implement `FromData`, `FromForm`. * HTTP 413 (Payload Too Large) errors are now returned when data limits are exceeded. (resolves #972) Hierarchical Limits * Data limits are now hierarchical, delimited with `/`. A limit of `a/b/c` falls back to `a/b` then `a`. Core * `&RawStr` no longer implements `FromParam`. * `&str` implements `FromParam`, `FromData`, `FromForm`. * `FromTransformedData` was removed. * `FromData` gained a lifetime for use with request-local data. * The default error HTML is more compact. * `&Config` is a request guard. * The `DataStream` interface was entirely revamped. * `State` is only exported via `rocket::State`. * A `request::local_cache!()` macro was added for storing values in request-local cache without consideration for type uniqueness by using a locally generated anonymous type. * `Request::get_param()` is now `Request::param()`. * `Request::get_segments()` is now `Request::segments()`, takes a range. * `Request::get_query_value()` is now `Request::query_value()`, can parse any `FromForm` including sequences. * `std::io::Error` implements `Responder` as `Debug<std::io::Error>`. * `(Status, R)` where `R: Responder` implements `Responder` by overriding the `Status` of `R`. * The name of a route is printed first during route matching. * `FlashMessage` now only has one lifetime generic. HTTP * `RawStr` implements `serde::{Serialize, Deserialize}`. * `RawStr` implements _many_ more methods, in particular, those related to the `Pattern` API. * `RawStr::from_str()` is now `RawStr::new()`. * `RawStr::url_decode()` and `RawStr::url_decode_lossy()` only allocate as necessary, return `Cow`. * `Status` implements `Default` with `Status::Ok`. * `Status` implements `PartialEq`, `Eq`, `Hash`, `PartialOrd`, `Ord`. * Authority and origin part of `Absolute` can be modified with new `Absolute::{with,set}_authority()`, `Absolute::{with,set}_origin()` methods. * `Origin::segments()` was removed in favor of methods split into query and path parts and into raw and decoded versions. * The `Segments` iterator is smarter, returns decoded `&str` items. * `Segments::into_path_buf()` is now `Segments::to_path_buf()`. * A new `QuerySegments` is the analogous query segment iterator. * Once set, `expires` on private cookies is not overwritten. (resolves #1506) * `Origin::path()` and `Origin::query()` return `&RawStr`, not `&str`. Codegen * Preserve more spans in `uri!` macro. * Preserve spans `FromForm` field types. * All dynamic parameters in a query string must typecheck as `FromForm`. * `FromFormValue` derive removed; `FromFormField` added. * The `form` `FromForm` and `FromFormField` field attribute is now named `field`. `#[form(field = ..)]` is now `#[field(name = ..)]`. Contrib * `Json` implements `FromForm`. * `MsgPack` implements `FromForm`. * The `json!` macro is exported as `rocket_contrib::json::json!`. * Added clarifying docs to `StaticFiles`. Examples * `form_validation` and `form_kitchen_sink` removed in favor of `forms`. * The `hello_world` example uses unicode in paths. * The `json` example only allocates as necessary. Internal * Codegen uses new `exports` module with the following conventions: - Locals starts with `__` and are lowercased. - Rocket modules start with `_` and are lowercased. - `std` types start with `_` and are titlecased. - Rocket types are titlecased. * A `header` module was added to `http`, contains header types. * `SAFETY` is used as doc-string keyword for `unsafe` related comments. * The `Uri` parser no longer recognizes Rocket route URIs.
So. Many. Changes. This is an insane commit: simultaneously one of the best (because of all the wonderful improvements!) and one of the worst (because it is just massive) in the project's history. Routing: * All UTF-8 characters are accepted everywhere in route paths. (#998) * `path` is now `uri` in `route` attribute: `#[route(GET, path = "..")]` becomes `#[route(GET, uri = "..")]`. Forms Revamp * All form related types now reside in a new `form` module. * Multipart forms are supported. (resolves #106) * Collections are supported in forms and queries. (resolves #205) * Nested structures in forms and queries are supported. (resolves #313) * Form fields can be ad-hoc validated with `#[field(validate = expr)]`. * `FromFormValue` is now `FromFormField`, blanket implements `FromForm`. * Form field values are always percent-decoded apriori. Temporary Files * A new `TempFile` data and form guard allows streaming data directly to a file which can then be persisted. * A new `temp_dir` config parameter specifies where to store `TempFile`. * The limits `file` and `file/$ext`, where `$ext` is the file extension, determines the data limit for a `TempFile`. Capped * A new `Capped` type is used to indicate when data has been truncated due to incoming data limits. It allows checking whether data is complete or truncated. * `DataStream` methods return `Capped` types. * `DataStream` API has been revamped to account for `Capped` types. * Several `Capped<T>` types implement `FromData`, `FromForm`. * HTTP 413 (Payload Too Large) errors are now returned when data limits are exceeded. (resolves #972) Hierarchical Limits * Data limits are now hierarchical, delimited with `/`. A limit of `a/b/c` falls back to `a/b` then `a`. Core * `&RawStr` no longer implements `FromParam`. * `&str` implements `FromParam`, `FromData`, `FromForm`. * `FromTransformedData` was removed. * `FromData` gained a lifetime for use with request-local data. * The default error HTML is more compact. * `&Config` is a request guard. * The `DataStream` interface was entirely revamped. * `State` is only exported via `rocket::State`. * A `request::local_cache!()` macro was added for storing values in request-local cache without consideration for type uniqueness by using a locally generated anonymous type. * `Request::get_param()` is now `Request::param()`. * `Request::get_segments()` is now `Request::segments()`, takes a range. * `Request::get_query_value()` is now `Request::query_value()`, can parse any `FromForm` including sequences. * `std::io::Error` implements `Responder` like `Debug<std::io::Error>`. * `(Status, R)` where `R: Responder` implements `Responder` by overriding the `Status` of `R`. * The name of a route is printed first during route matching. * `FlashMessage` now only has one lifetime generic. HTTP * `RawStr` implements `serde::{Serialize, Deserialize}`. * `RawStr` implements _many_ more methods, in particular, those related to the `Pattern` API. * `RawStr::from_str()` is now `RawStr::new()`. * `RawStr::url_decode()` and `RawStr::url_decode_lossy()` only allocate as necessary, return `Cow`. * `Status` implements `Default` with `Status::Ok`. * `Status` implements `PartialEq`, `Eq`, `Hash`, `PartialOrd`, `Ord`. * Authority and origin part of `Absolute` can be modified with new `Absolute::{with,set}_authority()`, `Absolute::{with,set}_origin()` methods. * `Origin::segments()` was removed in favor of methods split into query and path parts and into raw and decoded versions. * The `Segments` iterator is smarter, returns decoded `&str` items. * `Segments::into_path_buf()` is now `Segments::to_path_buf()`. * A new `QuerySegments` is the analogous query segment iterator. * Once set, `expires` on private cookies is not overwritten. (resolves #1506) * `Origin::path()` and `Origin::query()` return `&RawStr`, not `&str`. Codegen * Preserve more spans in `uri!` macro. * Preserve spans `FromForm` field types. * All dynamic parameters in a query string must typecheck as `FromForm`. * `FromFormValue` derive removed; `FromFormField` added. * The `form` `FromForm` and `FromFormField` field attribute is now named `field`. `#[form(field = ..)]` is now `#[field(name = ..)]`. Contrib * `Json` implements `FromForm`. * `MsgPack` implements `FromForm`. * The `json!` macro is exported as `rocket_contrib::json::json!`. * Added clarifying docs to `StaticFiles`. Examples * `form_validation` and `form_kitchen_sink` removed in favor of `forms`. * The `hello_world` example uses unicode in paths. * The `json` example only allocates as necessary. Internal * Codegen uses new `exports` module with the following conventions: - Locals starts with `__` and are lowercased. - Rocket modules start with `_` and are lowercased. - `std` types start with `_` and are titlecased. - Rocket types are titlecased. * A `header` module was added to `http`, contains header types. * `SAFETY` is used as doc-string keyword for `unsafe` related comments. * The `Uri` parser no longer recognizes Rocket route URIs.
How should Rocket handle certain parts of the Uri being missing? IIRC it's not technically invalid, at least according to spec, but it might cause issues when using the parsed Uri. Technically, according to RFC3986, a Uri looks sort of like There is no reason scheme can't be an empty string, and it looks like Rocket's parser allows this. The parse will generate The host futher breaks down into The origin futher breaks down into Rocket considers an empty string to be an invalid Uri, which is sort of an edge case for the RFC. This is probably for the best, since it's highly unlikely to be sent legitamely, and there are multiple ways to interpert it. Rocket considers an empty query string to be an error if and only if there is no scheme and there is no leading slash. E.g. tl;dr: It looks like rocket might have some issues, depending on how you interpert the RFC, and Rocket definitly has issues with query strings. |
@the10thWiz Your comment contains a lot of inaccuracies, unfortunately, so I'd like to address them for posterity.
This is not accurate. There are many forms of URIs specified via 3986, none of which look like this. The closest thing to what you've shown here is a URI-reference in 7230, but even that is different.
A
The
This is not a
All parts can be empty according to the RFC. Rocket does not store the
This is not correct. An origin-form URI must have a leading
This was wrong. An empty string is a valid URI.
You are conflating different URI forms for which the behavior should differ.
There is a single way to interpret the RFC, via its grammar, and Rocket had no issues with query strings. |
Background
Rocket 0.4 introduced Typed URIs and a new
Uri
type. Theuri!
macro and strictUri
validation is a valuable addition to Rocket's API and is a great example of Rocket's promise of safety and correctness, but there are a few problems with it due to deficiencies and/or bugs in Rocket and in browsers.This is a summary of the following issues and PRs:
Uri
does not allow anchors to be used (#
) #842 - URI References /Uri
type does not support anchors.Uri
s / generate absolute URIs fromuri!()
.Uri
issues, summarized #998 (comment)Bugs
Uri
does not appear to support fragments in any URI fromformat!
.Location
usesUri
and is therefore too strict about which kinds of URIs it accepts (no relative URI, no fragment)Location
and/orUri
Uri
is strict about characters such as{
needing to be percent-encoded{
and other characters unencoded, even when they ought to be encoded.{
or certain other relatively innocent charactesr in a plain HTML form submitted with the GET method, Rocket may refuse to process the request entirely.{
and some other commonly unencoded characters. Clearly document how this adheres or purposefully does not adhere to the relevant standard and why.>
isn't even legal in the HTTP header, according to hyper 0.10? Worth double-checking what's going on here.$
does not match%24
).Requests
uri!
only takes a string literal that must parse as anOrigin
as its mount point part.format!
.Uri
as the mount pointInto<Uri>
as a mount point. Maybe with a special case - string literals can be checked viaTryInto<Uri>
at compile time and raise an error if they are invalid.uri!
withOption
query parameters works surprisingly (e.g. Can't use uri! with routes that have optional query params #827 (comment))What next?
Recapitulating what I wrote in #924 (comment):
Rocket states that it adheres to RFC 7230, which relies on RFC 3986 for many of its URI-related definitions. But strictly adhering to RFC 3986 means that some URLs sent by browsers will be rejected as invalid. WHATWG's URL living standard is intended to replace previous URL standards, but it is still a moving target.
I think the first step to take is to decide if WHATWG's URL living standard is an acceptable specification for Rocket to follow at this time. If it is not suitable, careful and documented deviance from the currently followed specification(s) should be made for the few places that browsers do something different from our expectations.
There are approximately three decisions to be made as I see it:
Uri
with support for relative URIs and fragments, or loosening the requirements to useUri
in places such asLocation
At the end of the day, I don't want to review or merge a bunch of PRs that modify URI handling without a clear roadmap for doing so in a holistic way.
I will follow up with my own opinions on some of these choices, but I would like to get a feel for what other users of and contributors to Rocket expect as well.
The text was updated successfully, but these errors were encountered: