sergiobenitez / rocket Goto Github PK
View Code? Open in Web Editor NEWA web framework for Rust.
Home Page: https://rocket.rs
License: Other
A web framework for Rust.
Home Page: https://rocket.rs
License: Other
Rocket hides some information about the request when translating hyper request data into its own. Namely remote addr and httpversion are being dropped. These items may be quite useful. Please make them available in the rocket::Request along with the other data.
IE, allow the following (in the extreme case):
#[route(PUT, POST, DELETE, GET, path = "...")]
The documentation states that if the method is POST
, the Content-Type
is application/x-www-form-urlencoded
, and the first field is called _method
, the value of that field will be considered the request method instead of POST
. However, the framework still includes the _method
parameter in the request data, forcing the application to care that it might be there or error.
Have a route with the following code:
#[derive(FromForm)]
struct FormData {
form_data: String,
}
#[patch("/reproduce_bug", data = "<form_data>")]
fn reproduce_bug(form_data: Form<FormData>) -> &'static str {
assert_eq!("Form data", &form_data.get().form_data);
"Everything is fine"
}
Have a form with the following HTML, and submit it in the browser:
<form method="post" action="/reproduce_bug">
<input type="hidden" name="_method" value="patch" />
<input type="text" name="form_data" value="Form data" />
<input type="submit" value="Reproduce bug" />
</form>
The response "Everything is fine" is returned
=> _method=patch has no matching field in struct. => Error: Failed to parse value from form: BadParse
If deserializing the response data into a struct using FromForm
fails, Rocket by default will return 400 BAD REQUEST
, regardless of the reason that deserialization failed. This is the incorrect response code, unless deserialization failed due to malformed JSON. It is clearly defined in RFC 2616 as "The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.". For failures due to missing fields, unrecognized fields, fields of the wrong type, or pretty much any failure that isn't the result of the body being syntactically incorrect for the declared content-type, 422 UNPROCESSABLE ENTITY
should be used instead.
/
.GET /:
=> Matched: GET /
GET /:
=> Matched: GET /
=> => Outcome: Succcess
Outcome: Succcess
=> Response succeeded.
=> Response succeeded.
In particular, the => => Outcome: Succcess
line seems especially undesirable.
As the title says.
Do you plan to allow for pluggable features, such as an administrator UI that could be activated by simply adding 'rocket.register_plugin'?
I am thinking about a system where users have it easy to add or remove a feature (plugin) without having too much stuff to do.
In my mind plugins would have a init method which would do all the stuff a user would normally do to glue the code.
I wrote a view like this
#[get("/sample/<path..>")]
fn stream(path: PathBuf) -> Option<NamedFile> {
let filename = Path::new("/home/dawid/a_directory").join(path.clone());
println!("{:?}, {:?}", filename, path);
NamedFile::open(filename).ok()
}
I would expect path
to be just the part of URL after /sample/
, eg. localhost:8000/sample/dir/file.txt
would make path
be dir/file.txt
, however, path
becomes the whole URL after the hostname, in this example sample/dir/file.txt
🔧 Configured for development.
=> listening: localhost:8000
=> logging: Normal
🛰 Mounting '/':
=> GET /
=> GET /sample/<path..>
🚀 Rocket has launched from http://localhost:8000...
GET /sample/dir/file.txt:
=> Matched: GET /sample/<path..>
"/home/dawid/a_directory/sample/dir/file.txt", "sample/dir/file.txt"
=> Warning: Response was `None`.
=> Outcome: Failure
=> Warning: Responding with 404 Not Found catcher.
=> Response succeeded.
For example:
#[error(404)]
fn my_error() -> &str {
"Bad page."
}
fails to compile with:
error[E0061]: this function takes 0 parameters but 2 parameters were supplied
--> src/main.rs:1:1
|
1 | #![feature(plugin)]
| ^ expected 0 parameters
error: aborting due to previous error
It should be possible for application to handle errors. For example when binding to already used port, application could decide to bind to an alternative port.
Same applies for Rocket::ignite or any other function that can panic but does not have Result alternative.
Something that might be nice would be to allow query string parsing without having to use a full structure. As an example, consider a simple route to some path /hello?name=Sergio
. Currently, you need to write code that looks like:
struct Person {
name: String;
}
#[get("/hello?<person>")]
fn hello(person: Person) -> ... { ... }
It's not particularly great to have to create a structure for just a single field. Instead, we could use ad-hoc query params. The example could look like:
#[get("/hello?{name}")]
fn hello(name: String) -> ... { ... }
This is much cleaner. Several params could be matched like:
#[get("/hello?{name,age}")]
fn hello(name: String, age: Option<u8>) -> ... { ... }
It's not obvious how to make an object or some data available to all routes (ad-hoc). I'm thinking something like a DB connection pool object that would get passed to every route as an argument.
How would one do that with rocket?
It's currently not possible to tap into Rocket's logging infrastructure in any easy way. Of course, logging is important, and so it's necessary to have a nice, clean API for Rocket applications to log messages. Libraries like slog and log, the latter currently used by Rocket, provide nice, structured ways to do this. Look into restructuring Rocket's logging to expose their functionalities.
The current alternative is to simply println
to log.
Unfortunately, it looks like Rocket will need (or at least benefit from) at least some configuration. Aside from the obvious address and port, Rocket also requires:
It would be nice to split this into a separate file, too, so that changing configuration doesn't require a recompilation of the application. The file could contain a different key for each environment. Something like:
[dev]
log = debug
[stage]
address = "12.1.2.1"
port = 80
session_key = "MgbWFkZSISBVc..."
[prod]
address = "72.14.0.1"
port = 80
log = critical
session_key = "MgbWFkZSBmb3IgWW91ISBVc..."
headers = ["x-xss-protection: 1; mode=block", "x-frame-options: SAMEORIGIN"]
Each environment should have sane default. You shouldn't need a config file at all to develop.
The hello_world
main function could then look like:
Rocket::mount_and_launch("/hello", routes![hello]);
Rocket would then use the environment declared in a ROCKET_ENV
environment variable, or dev
by default. So, for example, in production, the binary could be run as:
ROCKET_ENV=prod ./hello_world
It would be neat if Rocket checks if the binary was compiled whether the binary was compiled in release
or debug
mode and warn if a debug
build is being used in production or if a release
build is being used during development. stage
should probably have no checks.
Running the forms example shows the following warning:
warning: `#[derive]` for custom traits is deprecated and will be removed in v1.15. Prefer using procedural macro custom derive
--> src\main.rs:12:10
|
12 | #[derive(FromForm)]
| ^^^^^^^^
Create the canonical Rocket authentication and authorization library.
The Content-Type
for String
is text/html
, but it is text/plain
for &str
. This seems like an unintuitive behavior, one would expect them to behave the same. Is there a reason for this?
Consider using semantic-rs
Have a route with the following code:
#[derive(FromForm)]
pub struct FormData<'a> {
form_data: &'a str,
}
#[post("/reproduce_bug", data = "<form>")]
fn reproduce_bug<'a>(form: Form<'a, FormData<'a>>) -> &'static str {
assert_eq!("A value with spaces", form.get().form_data);
"Everything is fine"
}
Create a template with the following HTML, and submit it using a browser
<form action="reproduce_bug" method="post">
<input type="text" name="form_data" value="A value with spaces" />
<input type="submit" value="Submit" />
</form>
The response "Everything is fine" is rendered
The value of the field is "A+value+with+spaces", causing the assertion to fail and a 500 to be returned.
This only applies to values of &str
and not String
, so I'm assuming that &str
is unconditionally pointing at the raw request data to avoid any allocation. Even if this behavior is documented (I couldn't find it mentioned), I think this is a surprising behavior and likely to be a common source of confusion. At absolute minimum this should produce some kind of error, but even that is a sub-optimal solution as it would lead to unexpected gotchas for users who do not test their inputs with spaces.
I'd expect the framework to be allocating internally if needed to properly handle decoding entities, and pointing at the raw request data if possible. I would never expect the framework to force me to care about encoding or content-type specific details unless I've specifically asked for some form of "raw" data.
I have not checked, but I suspect there are likely similar issues if the request is submitted with a charset other than UTF-8, and for JSON requests that would require additional decoding (such as an escaped backslash character).
The following features need to be stabilized before Rocket can compile on stable:
The following dependencies rely on nightly features and must be updated before Rocket can compile on stable:
pear
(SergioBenitez/Pear@333c9bf)Update (Feb 07, 2017): Added list of features used by Rocket.
Update (Feb 28, 2017): Added lookup_host
feature.
Update (Mar 21, 2017): pub_restricted
was stabilized!
Update (Mar 30, 2017): Added never_type
feature.
Update (Apr 16, 2017): Added concat_idents
feature.
Update (May 19, 2017): Added struct_field_attributes
and more_io_inner_methods
features.
Update (May 19, 2017): concat_idents
is no longer used.
Update (May 19, 2017): Added links to tracking issues.
Update (May 20, 2017): type_ascription
is no longer used.
Update (Jun 19, 2017): struct_field_attributes
was stabilized!
Update (Jun 24, 2017): Added try_trait
feature.
Update (Jul 1, 2017): more_io_inner_methods
was stabilized!
Update (Jul 9, 2017): associated_consts
was stabilized!
Update (Sep 7, 2017): lookup_host
is no longer used.
Update (Sep 14, 2017): drop_types_in_const
was stabilized!
Update (Sep 14, 2017): Added decl_macro
feature.
Update (Mar 26, 2018): conservative_impl_trait
, never_type
, and i128_type
were stabilized!
Update (Apr 22, 2018): Added fnbox
feature.
Update (Apr 26, 2018): never_type
stabilization was reverted (rust-lang/rust#50121).
Update (May 5, 2018): Swapped macro_reexport
for use_extern_macros
.
Update (Jul 29, 2018): Added crate_visibility_modifier
and try_from
features.
Update (Aug 5, 2018): custom_derive
is no longer used.
Update (Aug 17, 2018): use_extern_macros
was stabilized!
Update (Sep 3, 2018): const_fn
is no longer used.
Update (Sep 26, 2018): Added label_break_value
feature.
Update (Oct 9, 2018): Removed compiler plugin features (custom_attribute
, plugin
).
Update (Oct 9, 2018): Updated proc_macro
features.
Update (Mar 9, 2019): try_from
was stabilized!
Update (Apr 13, 2019): fnbox
is no longer used.
Update (Jul 9, 2019): never_type
is no longer used.
Update (Jul 19, 2019): decl_macro
is no longer used.
Update (Sep 9, 2019): specialization
is no longer used.
Update (Sep 10, 2019): label_break_value
is no longer used.
Update (Sep 18, 2019): try_trait
is no longer used.
Update (Sep 21, 2019): crate_visibility_modifier
is no longer used.
Update (May 19, 2020): proc_macro_hygiene
was stabilized!
Update (Jul 16, 2020): proc_macro_diagnostics
is no longer used.
Update (Jul 16, 2020): proc_macro_span
is no longer used.
Update (Jul 21, 2020): pear
was updated to be stable-compatible.
I tried to move routes in their own files/module for clarity and fall on a problematic routes!
macro error:
error[E0425]: unresolved name `static_rocket_route_info_for_index`
--> src/main.rs:11:33
|
11 | rocket::ignite().mount("/", routes![index, static_files]).launch();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved name
Here is my repository gomoku-web.
The most straightforward way would probably be to add a lanuch_ssl
method to Rocket that takes a T: hyper::net::SslServer
.
An idea I've been toying with is the addition of "resources" to Rocket. This would be similar to ActiveResource
in Rails. The motivation is that many apps are CRUD-like, and this would remove some of boilerplate of doing this as well as provide more structure when necessary.
As an example, consider a Todo
resource that be created, read, updated, and deleted. Here's what writing this might look like:
#[resource("todo")]
struct Todo {
description: String,
completed: bool
}
impl RocketResource for Todo {
type ReadResponse = DBResult<Either<Template, JSON<String>>>;
fn read(id: ID, format: ContentType) -> Self::ReadResponse {
let todo = try!(Database::find_todo(id));
if format.is_json() {
Either::B(JSON(todo.to_string()))
} else {
Either::A(Template::render("todo", &todo))
}
}
}
fn main() {
rocket::ignite().register("/", resources![Todo]).launch()
}
Note the associated type ReadResponse
. This can go away entirely once impl Trait
is allowed as a return type of trait methods. Then the return type can simply be impl Response
.
This example above would create four routes: POST todo/
, GET todo/<id>
, PATCH todo/<id>
, and DELETE todo/<id>
mapping to the four RocketResource
trait methods: create, read, update, delete
. PUT
should likely map to update
as well, though PATCH
is more appropriate. All of the RocketResource
methods should have default implementation that return a "not-implemented" error of some sort. In the example above, a GET todo/<id>
request would be routed to the read
method, running the logic in the example. Note also the ContentType
: resources will respond to any content-type, and the first content-type in the Accept
header will be passed in.
Here's a sketch of what the trait might look like:
trait RocketResource {
type CreateResponse = DefaultResponse;
type ReadResponse = DefaultResponse;
type UpdateResponse = DefaultResponse;
type DeleteResponse = DefaultResponse;
fn create(&self, format: ContentType) -> Self::CreateResponse {
DefaultResponse::new()
}
fn read(id: ID, format: ContentType) -> Self::ReadResponse {
DefaultResponse::new()
}
fn update(id: ID, format: ContentType) -> Self::UpdateResponse {
DefaultResponse::new()
}
fn delete(id: ID, format: ContentType) -> Self::DeleteResponse {
DefaultResponse::new()
}
}
Of note is the create
method which takes in an &self
. This is because Rocket will automatically parse the incoming request from forms and JSON. This means that the resource must implement FromForm
and Deserialize
. An alternative is to simply pass in Data
instead and let the user decide what to do. Another alternative is to pass in the accepted content-type to the resource
attribute: #[resource("todo", format = "application/json, text/html")]
. In this case, Rocket will return an error on any other kind of request format, and the user is guaranteed that ContentType
will always be one of those in the format
argument. And, the user will only have to implement FromForm
if they specify text/html
, and Deserialize
if they specify application/json
, which sounds nice.
Anyway, this is a developing thought. impl Trait
in method return types would make this really nice.
I noticed that the retrieve route is never mounted in the documentation. Everywhere is shown that you shouldn't forget to mount the new route you just made, but I missed this one. (it's not very hard to figure out by yourself but since all the others are shown I thought this one might as well be added)
Maybe it would be an idea to add it add the end of the tutorial just above the conlusion.
fn main() { rocket::ignite().mount("/", routes![index, upload, retrieve]).launch() }
I'm reffering to the pastebin example at: https://rocket.rs/guide/pastebin/
Enums are very useful in structures that represents forms, particularly as parses of radio
and option
fields. For example, if the HTML forms contains:
<label>Select:
<select name="select">
<option value="a">Value A</option>
<option value="b" selected>Value B</option>
<option value="c">Value C</option>
</select>
</label>
It would be nice to parse the select
field as an enum:
enum SelectValue {
A, B, C
}
In this example, "a" or "A"
would parse as SelectValue::A
, etc.
At present, Rocket does not support deriving FromFormValue
for any types. It would be nice to extend Rocket to automatically implement FromFormValue
to cover enums with nullary fields with an implementation as shown above.
Add something like Rail's flash or Django's messages. Use it in the Todo example.
The website currently has a slow-moving animation of clouds in the background. It makes the browser use a lot of CPU. This in turn makes fans spin faster and generate more noise, and probably drains my laptop’s battery.
It looks nice but I didn’t even notice it at first, until I started looking for what could possibly be using so much CPU. Therefore I think not much would be lost by making the clouds static.
To reproduce:
templates/posts/new.html.tera
Template::render("posts/new", &context)
Expected behavior:
The template is rendered
Actual behavior:
Error: Template 'posts/new' does not exist.
Test out using Homu.
There is a typo in the guide's Responses section. There is 'Reponder' instead of 'Responder' both in the side menu and the section title.
First of all, I wanna say good job. This project looks very promising.
One thing that is bugging me is how logging is set up. The way it remaps the levels from the standard debug/info/warn/error/critical to debug/normal/critical is not very intuitive and doesn't seem to be easy or allow plugging your own logger.
I think making logger pluggable and moving the current implementation to contrib would be really good. Is that something that makes sense to implement? I might give it a shot.
Variables passed to templates often have multiple types. Creating a one-off struct for this is certainly possible, but since ultimately the map!
macro is just "a hash map where the keys are strings and the values are something that can be serialized", that macro might be much more useful if the result was always HashMap<&str, json::Value>
, and the macro performed the serialization eagerly.
As the title says, please add a link to this repository to rocket.rs.
When running services in Mesosphere/Marathon cgroups, the framework sets a random number to PORT variable for the service. It would make Mesos deployments way easier if Rocket would respect the variable and override the service port with the value if set.
Browser's don't support anything but POST and GET. Still, it's nice to define an API as using the proper verb when possible. Use the well-known trick of adding a hidden form field with a known name and an http verb as a value. The field name should likely be '_method'. The value should be one of: "put", "delete", "patch".
Rocket should check for this field and use it when routing. A caveat is that Rocket may have to read the entire request to do this, which is slow and time consuming. A work around for this is to demand that _method be the first form value if it appears. Thankfully, the HTTP spec guarantees that the order in the request will match that in the form. Then, at worst, Rocket reads 8 bytes (_method=) more than it had to. Further, Rocket should only check when the request is POST of content type "application/x-www-form-urlencoded". It's unclear if "multipart/form-data" should be handled as well.
Unanswered: should this be optional? Should users be able to opt out of this feature? It seems like the upside is large and the downside is minimal, so it may make most sense to have this be a forced default.
It looks like the streams could be used to implement Server Sent Events, but it would be nice if there was some built in support.
There does not seem to be a link anywhere on the REPO to https://rocket.rs or you could just add it to the REPO description.
I'm having trouble figuring out how to compose routes. Is that possible? For example, I'd like to have a route that matches /auth
that ensures the user is authenticated, and then cedes control to another route. Is that possible?
I think I can use a RequestGuard
to do this, but that means that I would have to explicitly declare that on every route that starts with auth/...
, which is not DRY. Is that the best way to proceed? Happy to take these questions to a gitter/slack if that's available.
In order for a server to provide an external API it needs to be able to deal with Cross Origin Resource Sharing. These are done by web browsers sending a preflight OPTIONS request asking what resources it is allowed to access on the server, then after being given a resonse containing certain information in it's header a browser will send the actual GET/PUT/POST/etc query to the server, with no response it will not do this and cause Cross Site Scripting errors to be reported.
There are two obvious ways this can be handled, the first and simplest is to add an "OPTIONS" annotation of the same ilk as the existing HTTP Methods, this will allow users of Rocket to manually implement the correct preflight request handling for api endpoints that need to provide CORS.
The other is to provide some form of automatic CORS handling, this can be seen in a number of other libraries used for web server development (outside the realm of rust) such as spring.io (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html) or flasks (https://pypi.python.org/pypi/Flask-Cors).
W3 Specification on CORS: https://www.w3.org/TR/cors/
Example:
#[route(POST, path = "/todo", form = "<todo>")]
fn todo(todo: Todo) -> String {
...
}
generates...
fn rocket_route_fn_todo<'rocket>(_req: rocket::Request<'rocket>)
-> rocket::Response<'rocket> {
let todo: Todo =
{
...
};
let result = todo(todo); <-----
rocket::Response::new(result)
}
resulting in the error:
src/main.rs:1:1: 58:48 error: expected function, found Todo<'_>
While most of your codegen won't be satisfied by Macros 1.1, your custom derives will be. Using libsyntax for custom derive is currently deprecated (and sounds like it's slated for removal in 1.15). Would you be open to a PR to pull the custom derives out into a separate crate which uses macros 1.1?
For example when loading configuration from command line arguments, xml, ini or any other custom configuration Config's filepath is redundant. It would be nice if Config was just a trait that any application could tailor to application's needs.
First, this looks extremely promising at first glance. The top level DSL looks fantastic. Nice job.
Second, do you have plans for an async version?
Just saw you used Hyper. That may answer the question.
This is necessary for several reasons:
I think it would be useful if the internal template system—Handlebars or Tera—was exposed somewhere, so that it would be possible to make more template-specific modifications.
For some context, I'm using Handlebars, and I want to output a string containing HTML, however, Handlebars escapes strings by default. Hopefully this isn't a case of the XY problem, but I figure the best way I could solve this issue would be to acquire a reference to the static HANDLEBARS
object, so that I could mess with the escape_fn
settings or add a custom Helper.
CSRF is a thing. Protect against it automatically in forms.
Add a library response type, Stream<R: Writer> that takes an arbitrary writer and streams the written value.
Unanswered questions:
Should the bound of R be something else, such as Writer + Responder? Would that make things more or less convenient?
Test cases:
Stream a large file out.
Stream a large page out.
At present, Rocket reexports Hyper types under http:hyper
. Hyper causes a lot of issues, particularly when trying to optimize performance. A decision needs to be made on whether to stabilize these Hyper types, move away from Hyper (likely), or do something else all-together.
If we do move away from Hyper, here are things we should see to it that we re-implement or ensure the new library provides:
Date
header.Header
names and values.A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.