Fast and friendly HTTP server framework for async Rust

Related tags

HTTP Client tide
Overview

Tide

Serve the web

Tide is a minimal and pragmatic Rust web application framework built for rapid development. It comes with a robust set of features that make building async web applications and APIs easier and more fun.

Getting started

In order to build a web app in Rust you need an HTTP server, and an async runtime. After running cargo init add the following lines to your Cargo.toml file:

# Example, use the version numbers you need
tide = "0.16.0"
async-std = { version = "1.8.0", features = ["attributes"] }
serde = { version = "1.0", features = ["derive"] }

Examples

Create an HTTP server that receives a JSON body, validates it, and responds with a confirmation message.

) -> tide::Result { let Animal { name, legs } = req.body_json().await?; Ok(format!("Hello, {}! I've put in an order for {} shoes", name, legs).into()) }">
use tide::Request;
use tide::prelude::*;

#[derive(Debug, Deserialize)]
struct Animal {
    name: String,
    legs: u8,
}

#[async_std::main]
async fn main() -> tide::Result<()> {
    let mut app = tide::new();
    app.at("/orders/shoes").post(order_shoes);
    app.listen("127.0.0.1:8080").await?;
    Ok(())
}

async fn order_shoes(mut req: Request<()>) -> tide::Result {
    let Animal { name, legs } = req.body_json().await?;
    Ok(format!("Hello, {}! I've put in an order for {} shoes", name, legs).into())
}
$ curl localhost:8080/orders/shoes -d '{ "name": "Chashu", "legs": 4 }'
Hello, Chashu! I've put in an order for 4 shoes
$ curl localhost:8080/orders/shoes -d '{ "name": "Mary Millipede", "legs": 750 }'
number too large to fit in target type

See more examples in the examples directory.

Tide's design:

Community Resources

To add a link to this list, edit the markdown file and submit a pull request (github login required)
Listing here does not constitute an endorsement or recommendation from the tide team. Use at your own risk.

Listeners

  • tide-rustls TLS for tide based on async-rustls
  • tide-acme HTTPS for tide with automatic certificates, via Let's Encrypt and ACME tls-alpn-01 challenges

Template engines

Routers

Auth

Testing

Middleware

Session Stores

Example applications

  • dot dot vote
  • tide-example (sqlx + askama)
  • playground-tide-mongodb
  • tide-morth-example
  • broker (backend as a service)
  • tide-basic-crud (sqlx + tera)
  • tide-graphql-mongodb
    • Clean boilerplate for graphql services using tide, rhai, async-graphql, surf, graphql-client, handlebars-rust, jsonwebtoken, and mongodb.
    • Graphql Services: User register, Salt and hash a password with PBKDF2 , Sign in, JSON web token authentication, Change password, Profile Update, User's query & mutation, and Project's query & mutation.
    • Web Application: Client request, bring & parse GraphQL data, Render data to template engine(handlebars-rust), Define custom helper with Rhai scripting language.
  • surf
    • The Blog built on Tide stack, generated from tide-graphql-mongodb.
    • Backend for graphql services using tide, async-graphql, jsonwebtoken, mongodb and so on.
    • Frontend for web application using tide, rhai, surf, graphql_client, handlebars-rust, cookie and so on.
  • tide-server-example

Contributing

Want to join us? Check out our The "Contributing" section of the guide and take a look at some of these issues:

Conduct

The Tide project adheres to the Contributor Covenant Code of Conduct. This describes the minimum behavior expected from all contributors.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Comments
  • Session management

    Session management

    Work out a good story for session management in Tide. No need to reinvent the wheel here: this can probably follow an approach taken by an existing framework, like actix-web.

    design feature 
    opened by aturon 34
  • Middleware-based compression and decompression

    Middleware-based compression and decompression

    Provides compression middleware for outgoing HTTP responses and decompression middleware for incoming HTTP requests.

    Description

    These two middlewares do the following:

    • Automatically handle incoming request decompression for Content-Encoding headers that contain one or more applications of gzip, deflate, br, or zstd.
    • Encode outgoing responses with preferred encoding behavior for any incoming request with a valid Accept-Encoding header.
    • Allow one to define compression levels for each compression algorithm available.

    The decompression middleware actually depends on getting a mutable handle on the request from the context, hence why a new function was added here.

    Motivation and Context

    This closes https://github.com/rustasync/tide/issues/26.

    Tide currently has no way of handling encoded requests or handling outgoing request compression. This middleware is one approach to solve that problem, although there exist a multitude of approaches that may work. This middleware may also serve as a point of discussion for https://github.com/rustasync/team/issues/52.

    How Has This Been Tested?

    Tests are written! They're not very thorough, but that's because it's only to test their functionality. More rigorous tests for each of the stream encoders/decoders are in the async-compression crate.

    Types of changes

    • [ ] Bug fix (non-breaking change which fixes an issue)
    • [x] New feature (non-breaking change which adds functionality)
    • [ ] Breaking change (fix or feature that would cause existing functionality to change)

    Checklist:

    • [x] My change requires a change to the documentation.
    • [x] I have updated the documentation accordingly.
    • [x] I have read the CONTRIBUTING document.
    • [x] I have added tests to cover my changes.
    • [x] All new and existing tests passed.
    opened by fairingrey 23
  • Revamp Tide, dropping Extractors and simplifying the framework

    Revamp Tide, dropping Extractors and simplifying the framework

    This commit reworks Tide, with the goal of reducing the total number of concepts in the framework. The key new idea is to remove the notion of Extractors, which in turn allows us to remove or simplify several other concepts in Tide.

    We'll first lay out the new design, and then discuss the tradeoffs made in this simplification.

    Here's a full list of the concepts in Tide after this commit:

    | Concept | Description | | ----- | ----------- | | App | Builder for Tide applications | | Route | Builder for an individual route | | Endpoint | Trait for actual endpoints | | Context | The request context for an endpoint | | IntoResponse | A trait for converting into a Response | | Middleware | A trait for Tide middleware |

    Previously, the Endpoint trait was treated as a somewhat magical internal abstraction, and we used a macro to provide Endpoint implementations for actual endpoints (with varying numbers of extractor arguments).

    In this commit, an Endpoint is just an asynchronous function from a Context to a Response:

    pub trait Endpoint<AppData>: Send + Sync + 'static {
        /// The async result of `call`.
        type Fut: Future<Output = Response> + Send + 'static;
    
        /// Invoke the endpoint.
        fn call(&self, cx: Context<AppData>) -> Self::Fut;
    }
    

    For convenience, this trait is implemented for async functions that return any value that implements IntoResponse:

    impl<AppData, F, Fut> Endpoint<AppData> for F
    where
        F: Fn(Context<AppData>) -> Fut,
        Fut: Future
        Fut::Output: IntoResponse,
        // ...
    

    This implementation is in contrast to the macro-generated implementations we previously had, which allowed endpoints with varying numbers of Extractor arguments. The intent is for endpoints to perform their own extraction directly on the Context, as we'll see next.

    The Context type contains all of the request and middleware context an endpoint operates on. You can think of it as wrapping an http_service::Request with some additional data.

    It's easiest to understand Context through the APIs it provides. First, we have methods for getting basic http request information, mirroring the http APIs:

    impl<AppData> Context<AppData> {
        pub fn method(&self) -> &Method;
        pub fn uri(&self) -> &Uri;
        pub fn version(&self) -> Version;
        pub fn headers(&self) -> &HeaderMap;
    }
    

    The context also has a handle to application data, which typically would store database connection pools and other "application-global" state. This API replaces the old AppData extractor:

    impl<AppData> Context<AppData> {
        pub fn app_data(&self) -> &AppData {
            &self.app_data
        }
    }
    

    Similarly, we provide a direct API for extracting any "route parameters" (i.e. placeholders in the route URL), replacing the need for NamedSegment and the like:

    impl<AppData> Context<AppData> {
        pub fn route_param(&self, key: &str) -> Option<&str>;
    }
    

    Basic body extraction is likewise built in via Context methods, replacing the Str, Bytes, and Json extractors:

    impl<AppData> Context<AppData> {
        pub async fn body_bytes(&mut self) -> std::io::Result<Vec<u8>>;
        pub async fn body_string(&mut self) -> std::io::Result<String>;
        pub async fn body_json<T: serde::de::DeserializeOwned>(&mut self) -> std::io::Result<T>;
    }
    

    Looking at the message database example, we previously had endpoints like this:

    async fn new_message(mut db: AppData<Database>, msg: body::Json<Message>) -> String {
        db.insert(msg.clone()).to_string()
    }
    
    async fn set_message(
        mut db: AppData<Database>,
        id: head::Path<usize>,
        msg: body::Json<Message>,
    ) -> Result<(), StatusCode> {
        if db.set(*id, msg.clone()) {
            Ok(())
        } else {
            Err(StatusCode::NOT_FOUND)
        }
    }
    

    These endpoints would now be written something like this (where Error is intended as a general error type, convertible into a response):

    async fn new_message(cx: Context<Database>) -> Result<String, Error> {
        let msg = await!(cx.body_json())?;
    
        cx.app_data().insert(msg).to_string()
    }
    
    async fn set_message(cx: Context<Database>) -> Result<(), Error> {
        let msg = await!(cx.body_json())?;
    
        if cx.app_data().set(cx.route_param("id"), msg) {
            Ok(())
        } else {
            Err(StatusCode::NOT_FOUND)
        }
    }
    

    The endpoint code is a bit more verbose, but also arguably easier to follow, since the extraction (and error handling) is more clear.

    In addition, the basic extraction approach is more discoverable, since it operates via normal methods on Context.

    Part of the idea of the old Extractor trait was that Tide would provide an extensible system of extractors; you could always introduce new types that implement Extractor. But now most of the existing extractors are built-in Context methods. How do we recover extensibility?

    Easy: we use Rust's ability to extend existing types with new methods, via traits! (Note: this is directly inspired by the Gotham framework).

    Let's say we want to provide cookie extraction. Previously, we'd have a Cookies type that you could use as an endpoint argument for extraction. Now, instead, we can introduce a Cookies trait that's used to extend Context with new APIs:

    trait Cookies {
        fn cookies(&self) -> CookieJar;
    }
    
    impl<AppData> Cookies for Context<AppData> { ... }
    

    This pattern is called an "extension trait" -- a trait whose sole purpose is to extend an existing type with new methods. There are several nice properties of this approach:

    • The resulting extraction API is just a direct and natural as the built-in ones: just a method call on the Context object.

    • The methods that are available on Context are controlled by what traits are in scope. In other words, if you want to use a custom extractor from the ecosystem, you just bring its trait into scope, and then the method is available. That makes it easy to build a robust ecosystem around a small set of core Tide APIs.

    One of the major benefits of moving extraction into the endpoint body, rather than via Extractor arguments, is that it's much simpler to provide configuration. For example, we could easily provide a customized json body extractor that limited the maximum size of the body or other such options:

    impl<AppData> Context<AppData> {
        pub async fn body_json_cfg<T: serde::de::DeserializeOwned>(&mut self, cfg: JsonConfig) -> std::io::Result<T>;
    }
    

    As a result, we can drop much of the complexity in App around configuration.

    Following the spirit of the changes to extractors, response generation for non-standard Rust types is now just done via a free function:

    mod response {
        pub fn json<T: serde::Serialize>(t: T) -> Response { ... }
    }
    

    As before, there's a top-level App type for building up a Tide application. However, the API has been drastically simplified:

    • It no longer provides a configuration system, since extractors can now be configured directly.
    • It no longer allows for the middleware list to be customized per route; instead, middleware is set up only at the top level.

    These simplifications make the programming model much easier to understand; previously, there were inconsistencies between the way that middleware nesting and configuration nesting worked. The hope is that we can get away with this much simpler, top-level model.

    When actually adding routes via at, you get a Route object (which used to be Resource). This object now provides a builder-style API for adding endpoints, allowing you to chain several endpoints. Altogether, this means we can drop nested routing as well.

    The middleware trait is more or less as it was before, adjusted to use Context objects and otherwise slightly cleaned up.

    This commit also switches to using the route-recognizer crate, rather than the path-table crate, as the underlying routing mechanism. In addition to being more efficient, route-recognizer provides a more intuitive semantics for "ambiguous" routing situations. See issue #12 and issue #141 for more details.

    TODO

    • [x] Update docs
    • [x] Add back forms and cookies
    • [x] Update tests
    • [x] Update examples
    • [ ] Support App nesting
    opened by aturon 21
  • Build a larger example app

    Build a larger example app

    The current set of files in examples are extremely simple. It'd be great to build some more realistic (but still small) apps, both for documentation and as a way to try out the framework.

    good first issue 
    opened by aturon 18
  • Tide development plan

    Tide development plan

    As a follow-up to https://github.com/http-rs/tide/issues/325, I'd like to share the plan of how we can get Tide to a publishable state. This is not so much an issue to ask for input, but more to share what the plan is in case people are confused by what's going on.

    The core problem I think we currently have is one of my design: we have too many different crates, which makes it hard to publish & manage. In contrast: async-std has all functionality as part of a single repo, and adds new features behind the "unstable" feature flag. I think we should consolidate Tide into a single crate, and introduce an "unstable" feature flag.

    I think we should very much do the same, and the easiest way to get there is by resetting the repo by a few dozen commits to 4a538908f928aa1108473d1832073fc5cb799cc6, the commit right before all the "split" PRs landed.

    Note that this was fantastic work, implemented by @prasannavl. But I think this wasn't the right direction, which is entirely on me.

    From there we can move back through the commit log and start applying patches that have landed before. I estimate this to be a few uninterrupted days worth of work.


    From there the plan is less clear. But I have some idea of API changes I'd like to make. But before we do that we should probably create a new "snapshot" release so people can work off the latest master branch.

    I'd like to spend some effort during all this to write docs. We don't really have much right now, and I think that's holding us back. Unsure about the exact timeline, but it's something I think needs to be prioritized.

    This all sounds cool, how can I help?

    Unfortunately I think there's not much that can be done right now. The only separate issue that's coming up is https://github.com/http-rs/http-service/issues/31; so if you want to pick this up and help port http-service that would be fantastic. Please note though that I won't have any bandwidth to mentor this, and it won't be easy.

    Other than that this is mostly me effectively taking a lock on the project to move things forward. Once that's over I should have a better idea of which steps to take after, and tracking issues can be written. That's all further out still though. More updates in the future!

    Conclusion

    This is the current work plan I'm thinking of executing on. This means some big changes, but the path seems clear.

    opened by yoshuawuyts 17
  • Port to Tokio 0.2

    Port to Tokio 0.2

    Quickly sew this together to get started with Tokio 0.2 runtime. Due to an additional trait bound in http-service I cut Zstd support. Also I removed App::run, although I don't think it will really be missed since async main has become commonplace.

    Tested locally, appears to be working.

    opened by vorot93 17
  • [RFC] extensible routing in Tide

    [RFC] extensible routing in Tide

    This is the PR version of #271 to carry over the discussing in the PR format which allows better ways to have discussions around individual elements of the PR.

    Rendered

    opened by gameldar 17
  • Support for server-sent events

    Support for server-sent events

    Feature Request

    Support for server-sent events in Tide apps.

    Detailed Description

    Support for working creating server-sent event streams, easily, with Tide.

    Some more general server-sent events info:

    • Standard: https://html.spec.whatwg.org/multipage/#server-sent-events
      • Grammar: https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream
    • Using Server-Sent Events: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

    Context

    I would like to use server-sent events. I suppose other folks might want to as well.

    Possible Implementation

    Ideally, I would be able to have some method on Response or some sort of type that implements IntoResponse that sets the Content-Type: text/event-stream header for me, and takes a Stream<Item = ServerSentEvent> where ServerSentEvent is a struct with an optional event type string and a data string.

    Maybe it would automatically send heartbeats too, to keep the connection alive?

    design 
    opened by fitzgen 16
  • add more example application

    add more example application

    Motivation and Context

    To make easy for tide users adopt the framework, by using examples

    Checklist:

    • [ ] My change requires a change to the documentation.
    • [ ] I have updated the documentation accordingly.
    • [X] I have read the CONTRIBUTING document.
    • [ ] I have added tests to cover my changes.
    • [ ] All new and existing tests passed.
    opened by azzamsa 15
  • Allow the routing methods to open a static file

    Allow the routing methods to open a static file

    fn main() {
        let mut app = tide::App::new(());
    
       // Set static files context
        app.middleware(
              UrlPath::new()
                .prefix("/static/content/web/")
                .suffix(".html")
        );
    
      app.at("/").get(async || resource("index"));
      app.serve("127.0.0.1:7878");
    }
    

    P.S. Something similar to Rocket's NamedFile

    design feature 
    opened by sendilkumarn 15
  • can't get a simple post route to compile

    can't get a simple post route to compile

    I think what I'm doing matches an example I found, but I'm having no luck getting this simple example to compile. See the comments about the compiler errors I am seeing here: https://github.com/mvolkmann/rust-tide-demo/blob/master/src/main.rs#L58

    opened by mvolkmann 14
  • Chat link is invalid

    Chat link is invalid

    README.md lists https://discord.gg/x2gKzst as a Chat link for this project.

    After jumping through all the hoops and loops of creating an account with discord, including verifying ones email address and excelling in botanical skillz for the captcha, one is finally merely greeted with an error message reading: This invite may be expired, or you might not have permission to join.

    Maybe someone please could update the invitation link to a working one?

    opened by nospam3089 0
  • Fix sse log change to femme and kv_log_macro

    Fix sse log change to femme and kv_log_macro

    Fixed below two things in sse.

    1. Use the git version of tide, I had below error. Fixed it refere #889.
    error[E0433]: failed to resolve: could not find `error` in `log`
      --> tide/src/sse/endpoint.rs:51:22
       |
    51 |                 log::error!("SSE handler error: {:?}", err);
       |                      ^^^^^ could not find `error` in `log`
    
    error[E0433]: failed to resolve: could not find `error` in `log`
      --> tide/src/sse/upgrade.rs:22:18
       |
    22 |             log::error!("SSE handler error: {:?}", err);
       |                  ^^^^^ could not find `error` in `log`
    
    For more information about this error, try `rustc --explain E0433`.
    error: could not compile `tide` due to 2 previous errors
    
    1. example/sse.rs had build error.
    error[E0425]: cannot find function `start` in module `tide::log`
     --> examples/sse.rs:5:16
      |
    5 |     tide::log::start();
      |                ^^^^^ not found in `tide::log`
      |
    help: consider importing one of these items
      |
    1 | use femme::start;
      |
    1 | use logtest::start;
      |
    help: if you import `start`, refer to it directly
      |
    5 -     tide::log::start();
    5 +     start();
      |
    
    For more information about this error, try `rustc --explain E0425`.
    error: could not compile `tide` due to previous error
    
    opened by bluelief 0
  • Remove obsolete mention of Nightly in the Endpoint examples

    Remove obsolete mention of Nightly in the Endpoint examples

    Remove obsolete part of documentation which refers to attributes which were removed in commit 6eeb9b33 (which already removed a line from this paragraph)

    opened by dennisschagt 0
  • Expose the matched route as an extension on the request

    Expose the matched route as an extension on the request

    I considered adding it directly to the Request struct, though now I'm not sure anymore why I didn't, haha. I guess this PR is more of an RFC though. I would be happy with either implementation, but I do have a need for getting the route.


    Edit: I remembered now why I chose not to add it to the Request struct which is that it feels cleaner to have the Request struct only deal with the actual client-submitted request, and the concept of a matched route is purely a server-side abstraction.

    But since the Request already contains the list of captured route parameters I guess this point is a bit moot. Though I guess if this separation was wanted it wouldn't be unreasonable to remove the route_params struct member and turn that into an extension struct as well.

    opened by kyrias 2
  • Proposal: Refactor Middleware Execution / Next with Cursors and Async Recursion

    Proposal: Refactor Middleware Execution / Next with Cursors and Async Recursion

    I recently came across the salvo implementation for middleware (control flow) and I really like the use of the async_recursion combined with a simple cursor to allow middleware to run without any additional lifetimes. I think we could probably clean up the middleware to do something similar possibly.

        /// Call next handler. If get next handler and executed, returns true, otherwise returns false.
        ///
        /// If resposne's statuse code is error or is redirection, all reset handlers will skipped.
        #[inline]
        #[async_recursion]
        pub async fn call_next(&mut self, req: &mut Request, depot: &mut Depot, res: &mut Response) -> bool {
            if let Some(code) = res.status_code() {
                if code.is_client_error() || code.is_server_error() || code.is_redirection() {
                    self.skip_rest();
                    return false;
                }
            }
            if let Some(handler) = self.handlers.get(self.cursor) {
                self.cursor += 1;
                handler.clone().handle(req, depot, res, self).await;
                if self.has_next() {
                    self.call_next(req, depot, res).await;
                }
                true
            } else {
                false
            }
        }
    
    opened by nyxtom 1
  • Remove State and for<'a> as requirements from Middleware and Request

    Remove State and for<'a> as requirements from Middleware and Request

    Note: this would be a breaking change to the ecosystem as it eliminates the generics on all the traits (Server<State>, Listener<State>, Endpoint<State>, Request<State>, Next<'_>, Middleware<State>) however I think we could agree this is a net gain in ergonomics and allows users to add multiple .with_state via a new StateMiddleware (placing them in extensions on both request and response)

    There are a few existing #645 #715 and #643 related to improving ergonomics. As it currently stands the generic State is required on Request<State>, as well as Middleware<State>, Next<'_, State>, Endpoint<State>, Listener<State>, and Server<State> and other places. Since we already have the ability to use extensions for things like req.ext::<User>() I figured why not just merge the two and call it a day.

    I recognize that moving the State object out of the generics and into the Extensions may impact performance so this is something to consider - but I think this improvement in ergonomics and overall simplicity in navigating the code is worth it. The code now treats the state like any other middleware via the StateMiddleware<S>.

    When you call app.with_state(state) this simply wraps the <T: Clone + Send + Sync + 'static> in a StateMiddleware::new(state). The StateMiddleware will set the request extensions via set_ext(data.clone()) and also on the response after calling next.run.

    use tide::{Request};
    
    /// The shared application state.
    #[derive(Clone)]
    struct State {
        name: String,
    }
    
    // Define a new instance of the state.
    let state = State {
        name: "Nori".to_string()
    };
    
    // Initialize the application with state.
    let mut app = tide::with_state(state);
    app.at("/").get(|req: Request| async move {
        Ok(format!("Hello, {}!", &req.state::<State>().name))
    });
    app.listen("127.0.0.1:8080").await?;
    

    The new req.state::<State>() has an .expects on it when you call it which will automatically unwrap for you (or error out when you pass it a type that wasn't set in the middleware. The same goes for res.state::<State>() which I like because now you can get the application state in the response as well.

    The original .with_state on the Server was there for the generic type, but since state is now in middleware that function has been changed to require passing in the mutable server itself. The above example is the same as writing it below:

    use tide::{Request};
    
    /// The shared application state.
    #[derive(Clone)]
    struct State {
        name: String,
    }
    
    // Define a new instance of the state.
    let state = State {
        name: "Nori".to_string()
    };
    
    // Initialize the application with state.
    let mut app = tide::new();
    app.with_state(state);
    // app.with_state(state2);
    app.at("/").get(|req: Request| async move {
        Ok(format!("Hello, {}!", &req.state::<State>().name))
    });
    app.listen("127.0.0.1:8080").await?;
    

    The nice thing about this is now you can add multiple different state types rather than force everything into a single struct. You can also directly add the StateMiddleware by calling app.with(StateMiddleware::new(s)) although in practice I think people will stick to app.with_state.

    Overall I think this is a great gain in ergonomics!

    Update on Next<'_>

    For this I may separate out into a different pull request but let me know

    I've also now refactored the use of Next<'_> to no longer need the extra lifetimes in favor of Arc for endpoints and middleware. Selection routing will return these as well rather than explicit borrows with lifetime requirements. This simplifies some of the code and opens up the possibility for passing along impl Middleware function closures.

    // This is an example of a function middleware that uses the
    // application state. Because it depends on a specific request state,
    // it would likely be closely tied to a specific application
    fn user_loader(mut request: Request, next: Next) -> Pin<Box<dyn Future<Output = Result> + Send>> {
        Box::pin(async {
            if let Some(user) = request.state().find_user().await {
                tide::log::trace!("user loaded", {user: user.name});
                request.set_ext(user);
                Ok(next.run(request).await)
            // this middleware only needs to run before the endpoint, so
            // it just passes through the result of Next
            } else {
                // do not run endpoints, we could not find a user
                Ok(Response::new(StatusCode::Unauthorized))
            }
        })
    }
    

    This is the new format as it no longer requires <State> or Next<'_, State> or Next<'_> or any user_loader<'a> as the requirement for<'a> is now gone. I will update this pull request to also support the use of a function closure. This will solve https://github.com/http-rs/tide/issues/854 as well through the use of impl Middleware

    async fn auth_middleware(request: tide::Request, next: tide::Next) -> tide::Result {
        let authenticated = match request.header("X-Auth") {
            Some(header) => header == "secret_key",
            None => false,
        };
    
        if authenticated {
            Ok(next.run(request).await)
        } else {
            Ok(tide::Response::new(tide::StatusCode::Unauthorized))
        }
    }
    

    In the process I've simplified the use of the Next so that it can be used as an Endpoint (rather than have a separate MiddlewareEndpoint). This was useful for the Server to wrap the existing Next selection and add in its own middleware.

    opened by nyxtom 1
Releases(v0.17.0-beta.1)
  • v0.17.0-beta.1(Dec 6, 2021)

  • v0.16.0(Jan 29, 2021)

    tide is a pragmatic Rust web app framework built for rapid development. It comes with a robust set of features that make building async web apps and APIs easier and more fun. It is part of the http-rs project and a counterpart to the surf HTTP client. Check out the docs or join us on Zulip.

    Overview

    This release includes a new serve_file method on Route, more ToListener implementations, and new initiatives.

    Route::serve_file

    Tide has had support for Route::serve_dir for several releases now. However sometimes it's nice to be able to quickly serve a file from a single file from a route. For that purpose we're introducing Route::serve_file today.

    #[async_std::main]
    async fn main() -> Result<(), std::io::Error> {
        let mut app = tide::new();
        app.at("/").serve_file("public/index.html")?;
        app.listen("127.0.0.1:8080").await?;
        Ok(())
    }
    

    This isn't the end of the road for asset serving in tide though; we recognize the need to support more complex asset pipelines in production settings. We expect to eventually kick off an initiative around this; but in the meantime Route::serve_file is a nice convenience for those who want it.

    More ToListener implementations

    Tide's Server::listen function operates using a ToListener trait, which works much like std::net::ToSocketAddr: it's implemented on a variety of strings, tuples, and concrete types to provide a great deal of flexibility in how it's initialized.

    In this patch we're introducing ToListener on three more types: &String, (String, u16) and (&String, u16). This allows for much easier integration with CLI parsers such as structopt, requiring fewer conversions:

    use structopt::StructOpt;
    
    #[derive(structopt::StructOpt)]
    struct Opts {
        host: String,
        port: u16,
    }
    
    #[async_std]
    async fn main() -> tide::Result<()> {
        let opts = Opts::from_args();
        let mut app = tide::new();
        app.listen((opts.host, opts.port)).await?;  // this now works without conversions!
        Ok(())
    

    New Initiatives

    Last week we released the last minor version of the http-types 2.x family, and the merge window for http-types 3.x has opened up. This is an effort which will take several weeks of work and planning to see through to success; but we're excited being able to smooth out some of the rough patches we've uncovered in http-types over the past year. The work here has been scoped out, and we've started to make good progress on it.

    Once that's over however, it is probably time to revisit the question of a Tide 1.0 release. Over the past year Tide's core design has mostly remained stable; but added plenty of features improvements. However we're noticing two things:

    1. Not all facilities included in Tide are quite where we'd like them to be.
    2. Tide being on 0.x and publishing regular breaking releases makes it hard to build ecosystem libraries

    For that purpose we're likely to split some parts of Tide we're less happy about into libraries (looking at you tide::log), and spend some time iterating them outside of the release cycle of Tide itself. We hope this will enable more people to experiment in the ecosystem, mature features faster, and enable more people to contribute to Tide and the wider http-rs ecosystem.


    Added

    • Add serve_file method to route #725
    • Add more ToListener implementations #749
    • Add simple state example #742
    • docs: add tide-sqlx to readme #754

    Fixed

    • In serve_dir, only strip the prefix from the start of the path once. #734
    • docs: Do not inline docs from deps that drift #753
    • docs: Fix port in curl invocation in example. #764
    • docs: Update version numbers of deps for README example #765
    • docs: Add missing serde requirement in example #782
    • docs: Fix Route::serve_dir docs #750

    Internal

    • deps: Reduce dependencies when cookies not enabled #780
    • deps: Update async-h1 to v2.3.0 #767
    • deps: Update log to use kv_unstable_std instead of std #776
    • ci: Run clippy, docs and fmt checks on stable #756
    Source code(tar.gz)
    Source code(zip)
  • v0.15.1(Jan 28, 2021)

    This patch fixes an issue with an unstable feature flag in the log dependency which was causing compilation to fail.

    Fixed

    • Update log to use kv_unstable_std instead of std #776
    Source code(tar.gz)
    Source code(zip)
  • v0.15.0(Nov 13, 2020)

    This patch adds Server::bind, SessionMiddleware::with_cookie_domain, and a new optional cookies feature.

    Server::bind

    Tide v0.15.0 introduces a new way to start servers: Server::bind. This enables separatining "open the socket" from "start accepting connections" which Server::listen does for you in a single call.

    This was introduced as a way to enable users to log messages after ports were successfully opened. But it can also be used to synchronize server initialization. For example: your application may want to connect to a database, a cache, and open an HTTP connection. With Server::bind you can start the connection, but wait to handle inbound traffic until all other components of the server have started up.

    When Server::bind is called, it returns an instance of Listener which is able to return information on all ports that are being listened on. By default Server::listen logs these out, but when manually calling Server::bind you get control on how to log this info.

    For now ListenInfo only includes a few basics such as the address that's being listened on, and whether the connection is encrypted. But as we seek to stabilize and integrate tide-rustls into tide, we may include more info on the encryption settings. And perhaps in the future we'll include more information on the server's routes too. But for now this serves as an entry point for all that.

    use tide::prelude::*;
    
    let mut app = tide::new();
    app.at("/").get(|_| async { Ok("Hello, world!") });
    let mut listener = app.bind("127.0.0.1:8080").await?;
    for info in listener.info().iter() {
        println!("Server listening on {}", info);
    }
    listener.accept().await?;
    

    SessionMiddleware::with_cookie_domain

    Our session middleware now supports a with_cookie_domain method to scope a cookie to a specific domain. We already support various cookie options when constructing the session middleware, and now we support scoping the domain as well.

    let SECRET = b"please do not hardcode your secret";
    let mut app = tide::new();
    
    app.with(SessionMiddleware::new(MemoryStore::new(), SECRET)
            .with_cookie_name("custom.cookie.name")
            .with_cookie_path("/some/path")
            .with_cookie_domain("www.rust-lang.org") // This is new.
            .with_same_site_policy(SameSite::Lax)
            .with_session_ttl(Some(Duration::from_secs(1)))
            .without_save_unchanged(),
    );
    

    http-types typed headers

    We've been doing a lot of work on typed headers through http-types, which is the HTTP library underpinning both tide and surf. We're getting close to being done implementing all of the specced HTTP Headers, and will then move to integrate them more closely into Tide. You can find the release notes for http-types here.

    Added

    • Add Server::bind #740
    • Add with_cookie_domain method to SessionMiddleware #730
    • Add an optional cookies feature #717

    Fixed

    • Fix param documentation #737
    • Fix port in README #732

    Internal

    • Lints #704
    Source code(tar.gz)
    Source code(zip)
  • v0.14.0(Oct 16, 2020)

    Documentation

    This patch introduces a several feature flags to opt-out of dependencies, a reworked rustdoc landing page, and a variety of bug fixes. Over the past few months we've been hard at work on Surf v2.0.0, and have made a lot of progress on http-types' typed headers. We hope to start bringing some of this work over into Tide soon.

    Behind the scenes we're also hard at work at improving our processes. Both Tide and the wider http-rs project have really taken off, and our biggest challenge in ensuring we correctly prioritize, communicate, and empower people who want to get involved. We don't have specifics we can share yet, but it's something we're actively working on with the team. Because as Tide and http-rs grow, so must our processes.

    Added

    • Implement Endpoint for Box<dyn Endpoint> #710
    • Add a http_client::HttpClient implementation for tide::Server #697
    • Introduce a logger feature to optionally disable the femme dependency #693
    • Add Server::state method #675

    Changed

    • Remove parsing from Request::param #709
    • Rework landing docs #708
    • Log: display client error messages when possible as warnings #691
    • Make all listeners conditional on h1-server feature #678

    Fixed

    • Logger: properly print debug from errors #721
    • Fix missing as_ref that caused boxed endpoints to enter an infinite loop #711
    • Bugfix, route prefix was always set to false after calling nest #702
    • Fix a broken documentation link #685

    Internal

    • Upgrade deps #722
    • CI, src: run clippy on nightly, apply fixes #707
    • Update to latest Surf alpha in tests #706
    • Fix .github #705
    • Add driftwood to middleware section #692
    • Refactor README #683
    • Main branch renamed to main #679
    • Bump version number in README.md #672
    • Add community resources to readme instead of wiki #668
    Source code(tar.gz)
    Source code(zip)
  • v0.13.0(Jul 31, 2020)

    Docs

    This release introduces first-class support for sessions, fixes a long-standing bug with our default middleware, clarifies our stability guarantees, and renamed the API to register middleware through.

    Sessions

    We're excited to announce initial support for sessions in Tide. This feature enables Tide applications to associate multiple separate requests as belonging to the same origin. Which is a pre-requisite to build common web-app features such as user accounts, multi-request transactions, and channels.

    Tide sessions are generic over backend stores and signing strategy. It builds on the newly released async-session 2.0.0 library, which is a set of common traits that types that make up a session. But beyond that, much of it is implementation specific.

    Tide ships with a memory and cookie store by default. However we have also published several convenient session store implementations for common databases, providing a nice selection to choose from:

    Using "Redis" as the backing session store for Tide is as easy as writing 3 lines and including a dependency in your Cargo.toml:

    use async_redis_session::RedisSessionStore;
    use tide::sessions::SessionMiddleware;
    use tide::{Redirect, Request};
    
    #[async_std::main]
    async fn main() -> tide::Result<()> {
        let mut app = tide::new();
    
        // Create a Redis-backed session store and use it in the app
        let store = RedisSessionStore::new("redis://127.0.0.1:6379")?;
        let secret = std::env::var("SESSION_SECRET").unwrap();
        app.with(SessionMiddleware::new(store, secret.as_bytes()));
    
        app.at("/").get(|mut req: Request<()>| async move {
            // Store a counter in the session; increment it by one on each visit
            let session = req.session_mut();
            let visits: usize = session.get("visits").unwrap_or_default();
            session.insert("visits", visits + 1).unwrap();
    
            // Render a page that shows the number of requests made in the session
            let visits: usize = req.session().get("visits").unwrap();
            Ok(format!("you have visited this website {} times", visits))
        });
    
        // Close the current session
        app.at("/reset").get(|mut req: Request<()>| async move {
            req.session_mut().destroy();
            Ok(Redirect::new("/"))
        });
    
        // Start the server
        app.listen("127.0.0.1:8080").await?;
        Ok(())
    }
    

    It's still early for Tide sessions. But we're incredibly excited for how convenient it already is, and excited for the possibilities this will enable!

    Renaming Server::middleware to Server::with

    This patch renames Server::middleware to Server::with in order to streamline much of the middleware APIs.

    // After this patch
    let mut app = tide::new();
    app.with(MyMiddleware::new());
    app.at("/").get(|_| async move { Ok("hello chashu") });
    app.listen("localhost:8080").await?;
    
    // Before this patch
    let mut app = tide::new();
    app.middleware(MyMiddleware::new());
    app.at("/").get(|_| async move { Ok("hello chashu") });
    app.listen("localhost:8080").await?;
    

    A small change, but ever so convenient.

    No more duplicate log messages

    Ever since we introduced application nesting we've had issues with default middleware running twice. This patch fixes that for our logging middleware in two ways:

    1. We've introduced a logger Cargo feature to disable the default logging middleware #661
    2. We now track calls to the logger inside the state map to ensure it's only called once per app #662

    There may be room to optimize this further in the future, and perhaps extend the same mechanisms to work for more built-in middleware. But for now this patch resolves the most common issues people were reporting.

    Clarification on stability

    Tide has been deployed to production in many places: independent authors taking control of their publishing pipelines, software professionals building internal tooling, and enterprises running it in key parts of their infrastructure.

    In past Tide releases shipped with a warning that actively recommended against using it in any critical path. However we've chosen to no longer include that warning starting this release. Much of the work at the protocol layer and below has completed, and we've received positive reports on how it performs.

    For the foreseable future Tide will remain on the 0.x.x semver range. While we're confident in our foundations, we want to keep iterating on the API. Once we find that work has slowed down we may decide when to release a 1.0.0 release.

    Added

    • Added From<StatusCode> for Response #650
    • Added a feature-flag to disable the default logger middleware #661
    • Added impl Into<Request> for http_types::Request #670

    Changes

    • Rename Server::middleware to Server::with #666
    • Relax Sync bound on Fut required on sse::upgrade #647
    • Consistency improvements for examples #651
    • Bumped version number in docs #652
    • Add enable logging in README example #657
    • Remove "experimental" warning #667

    Fixes

    • Guarantee the log middleware is only run once per request #662

    Internal

    • Enable clippy for tests #655
    • Reorder deps in cargo.toml #658
    Source code(tar.gz)
    Source code(zip)
  • v0.12.0(Jul 17, 2020)

    Docs

    This release includes 35 PRs merged over the course of the last month. Most notably we've streamlined how errors are propagated inside middleware, introduced a new ResponseBuilder type, State must now be Clone, and we are introducing an extensible API for Server::listen.

    ResponseBuilder

    Returning responses from endpoints is often different from operating on response in middleware. In order to make it easier for people to author responses we're introducing tide::ResponseBuilder in this patch!

    You can create a new response builder by calling Response::builder and passing it a status code. This enables writing some really concise endpoints:

    app.at("/").get(|_| async {
        let res = Response::builder(203)
            .body(json!({ "hello": "cats!" }))
            .header("X-Nori", "me-ow")
            .header("X-Chashu", "meewwww");
        Ok(res)
    })
    

    This sets Tide up really nicely for the future too; once we have async closures, and a resolution for Ok-wrapping (fingers crossed) this will be even more concise. We're excited for developments in the language!

    Server listen

    Tide now supports extensions for App::listen. This patch introduces a new Listener trait that is implemented for std types such as TcpStream, SocketAddr and UnixStream. But can also be implemented by users of Tide to provide custom transports.

    In particular, what this enables us to do is to start trialing TLS support in external crates. We'll soon have tide-rustls available as an external crate that will enable building TLS-terminating Tide servers:

    let mut app = tide::new();
    let listener = TlsListener::build()
        .addrs("localhost:4433")
        .cert(cert)
        .key(key);
    app.listen(listener).await?;
    

    In addition we're shipping tide::listener::ConcurrentListener, a convenient constructor to have a single server respond to incoming requests from multiple transports. For example, some applications may want to listen on both IPv4 and IPv6. With ConcurrentListener that's possible:

    use std::net::{Ipv4Addr, Ipv6Addr};
    use tide::listener;
    
    let mut app = tide::new();
    let mut listener = listener::ConcurrentListener::new();
    listener.add((Ipv4Addr::new(127, 0, 0, 1), 8000));
    listener.add((Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8000));
    app.listen(listener).await?;
    

    State must be Clone

    One problem we had for a while was that when manually nesting or parallelizing applications, State would be wrapped in an Arc multiple times. In this patch we're solving that by providing people with more control around how State is shared by requiring State to implement Clone.

    In most existing applications State can be made Clone by manually wrapping it in an Arc::new. But it's also possible to wrap individual fields in an Arc and deriving `Clone for the whole struct, as we did in one of our examples:

    // Example state before this patch.
    struct State {
        users: RwLock<Vec<User>>,
    }
    
    // Example state after this patch.
    #[derive(Clone)]
    struct State {
        users: Arc<RwLock<Vec<User>>>,
    }
    

    There is no right answer how to structure State; but we wanted to enable people to factor it in the way that makes most sense for their applications.

    Using of async-trait

    We've migrated all of our traits to use async-trait. This should make it easier to author Middleware implementations. For convenience Tide re-exports as tide::utils::async_trait.

    Changes in middleware error handling

    Before this patch, calling next().await? in middleware would return a Result<Response>. The idea was that the Err variant could freely be thrown up the middleware stack, and transformed into a Response at the top of the stack. However in practice this didn't turn out great: middleware such as CORS needed to manipulate the Err path to ensure the right headers were set. And we didn't provide an interface for this.

    So instead this patch changes the way we handle errors in middleware. We still enable ? to be used inside middleware, but between each middleware we convert Result<Response, tide::Error> into a Response, and if an error occurred, we populate the newly introduced Response::error field.

    This means that middleware can always assume there is a valid Response coming through, and no longer needs to check both Ok and Err branch returned by next().await. An example:

    /// Before this patch: need to check both branches.
    async fn my_middleware<State>(req: Request<State>, next: Next) -> Result<Response> {
        println!("before");
        match next().await {
            Err(err) => {
                println!("status code {}", err.status());
                Err(err)
            }
            Ok(res) => {
                println!("status code {}", res.status());
                Ok(res)
            }
        }
    }
    
    /// With this patch: there's only a single branch to operate on.
    async fn my_middleware<State>(req: Request<State>, next: Next) -> Result<Response> {
        println!("before");
        let res = next().await;
        println!("status code {}", res.status());
        Ok(res)
    }
    

    Note: neither of these examples will quite compile until we have async closures, but it serves to illustrate the point.

    Added

    • Add a doc example for Request::body_form #631
    • Add a doc example for Request::query #630
    • Add an upload example #619
    • Add extensible entrypoint for Server::listen #610
    • Add From<Body> for Response #584
    • Add ResponseBuilder #580
    • Add Response::error #570
    • Add CORS headers to error responses #546

    Changed

    • Use async_trait to simplify async signatures #639
    • Also include port and host in the log string #634
    • Don't panic on missing path param #615
    • Return a result from sse::Sender::send #598
    • Relax the lifetime requirements of Server::at #597
    • In middleware Next::run now returns Response instead of Result<Response> #570
    • Rename tide::middleware to tide::utils #567
    • Require State to be Clone #644

    Fixed

    • Make ServeDir return 404 if file does not exists #637
    • Remove #[must_use] for Response::set_status() #612
    • Do not await the spawned task in Server::listen #606
    • Allow route based function middlewares #600
    • Fix CORS middleware to retain cookies #599
    • Enable SSE senders to break out of loops #598
    • Remove extra unwraps from insert_header calls #590 #588 #583
    • Don't crash the server when there's a listen error #587
    • Add CORS headers to error responses #546

    Internal

    • Remove executable mode from lib.rs #635
    • Update to async-sse 4.0.0 #632
    • Comment cleanup fixes #622
    • Add clippy to ci #618
    • Restore .github docs #616
    • Use route-recognizer 0.2 #607
    • Introduce an extension trait for testing servers #601
    • Update the readme with the latest versions #594
    • Fix tempfile usage in tests #592
    • Fix CI #589
    • Remove unnecessary moves from route handlers #581
    Source code(tar.gz)
    Source code(zip)
  • v0.11.0(Jun 12, 2020)

    This patch introduces several minor features and fixes. This is a small release which picks up on some of the details we missed in our last few large releases.

    Added

    • Added Request::set_body #579
    • Added impl From<Body> for Response #584

    Changed

    • Response::set_status no longer takes and returns self #572

    Fixes

    • Fixed an issue with unwrap in SSE https://github.com/http-rs/tide/pull/588
    • Fixed an unwrap issue in Endpoint https://github.com/http-rs/tide/pull/590

    Internal

    • Delete the unmaintained CHANGELOG.md file #565
    • Renamed cx to req #582
    • Fix failing dependency on CI #589
    • Use tide::Response interface in the sse endpoint #583
    • Remove unnecessary moves from route examples #581
    Source code(tar.gz)
    Source code(zip)
  • v0.10.0(Jun 5, 2020)

    Docs

    This release updates tide's Request and Response types to match http_types's Request and Response, a new Server::listen_unix method to listen on Unix Domain Sockets, and the ability to return json! literals directly from endpoints.

    Added

    • Added Server::listen_unix #531
    • Added Request::peer_addr #530
    • Added Request::local_addr #530
    • Added Request::remote #537
    • Added Request::host #537
    • Added Request::header_mut #537
    • Added Request::append_header #537
    • Added Request::remove_header #537
    • Added Request::iter #537
    • Added Request::iter_mut #537
    • Added Request::header_names #537
    • Added Request::header_values #537
    • Added Request::query #537
    • Added Request::content_type #537
    • Added warning about domain/path inconsistencies to remove_cookie #533
    • Added an optional name method to Middleware #545
    • Added AsRef/AsMut<Headers> for Request/Response #553
    • Added Request::take_body #550
    • Added Response::swap_body #562
    • Added Response::iter #550
    • Added Response::iter_mut #550
    • Added Response::header_names #550
    • Added Response::header_values #550
    • Added Response::content_type #550
    • Added Response::set_content_type #550
    • Added Response::header_mut #562
    • Added tide::{After, Before} middleware constructors #556
    • Added support for returning JSON literals from endpoints #523
    • Response::new now accepts u16 as well as StatusCode as arguments #562
    • Added a new convert submodule which holds various conversion-related types, including serde #564

    Changed

    • Renamed Request::uri to Request::url #537
    • Request::body_bytes now returns tide::Result #537
    • Request::body_string now returns tide::Result #537
    • Request::body_json now returns tide::Result #537
    • Request::body_form now returns tide::Result #537
    • Request::set_ext no longer takes and returns Self #537
    • Use http_types::mime instead of the mime crate #536 -
    • Renamed Reponse::set_cookie to Response::insert_cookie #562
    • Various Response methods no longer return Self #562
    • Renamed Response::set_header to Response::insert_header #562
    • Renamed Reponse::set_ext to Response::insert_ext #562

    Removed

    • Removed Response::redirect in favor of tide::Redirect #562
    • Removed Response::{body_string, body_form, body_json, body} in favor of Response::set_body #562

    Fixed

    • Update docs from Request::local to Request::ext #539
    • Creating a middleware directly from a function is now possible again #545
    • Fixed wildcard tests #518

    Internal

    • Fix unix tests #552
    • Update http-types to 2.0.1 #537
    • Update async-sse to v3.0.0 #559
    • Use query from http_types #555
    • Fix tests on master #560
    • Remove unused serde_qs dependency #569
    • Uncomment and fix response tests #516
    Source code(tar.gz)
    Source code(zip)
  • v0.9.0(May 23, 2020)

    This patch updates http-types to 2.0.0, removes http-service in favor of Server::respond, and adds an all-new Redirect struct.

    http-types 2.0

    Read the http-types changelog for a full rundown of changes. But the biggest change for Tide is that working with headers in Tide is becoming a lot easier. To see all the changes in action, compare what it was like to compare a header with how it is now:

    // http-types 1.x
    assert_eq!(req.header(&"X-Forwarded-For".parse().unwrap()), Some(&vec!["127.0.0.1".parse().unwrap()]));
    
    // http-types 2.x
    assert_eq!(req.header["X-Forwarded-For"], "127.0.0.1");
    

    Constructing headers from string literals, comparing to string literals, using [] to access by name — this should make it much easier to work with Tide!

    Server::respond

    http-service has been a tricky one. It originally served as a simplified wrapper around hyperium/hyper with a streaming Body type with the potential to abstract to other backends as well. But as we've evolved Tide and the http-rs ecosystem, we found http-service becoming more a hurdle than a help.

    In this patch we're removing http-service from Tide and introducing Server::respond as its replacement. This not only makes Tide easier to maintain, it makes it easier to use with any other backend than ever before. It also provides convenient way to unit test Tide apps as well:

    use tide::http::{self, Url, Method};
    
    #[async_std::test]
    async fn hello_world() -> tide::Result<()> {
        let mut app = tide::new();
        app.at("/").get(|_| async move { Ok("hello world")} );
    
        let req = http::Request::new(Method::Get, Url::parse("http://computer.soup")?);
        let mut res: http::Response = app.respond(req).await?;
    
        assert_eq!(res.body_string().await?, "hello world".to_owned());
        Ok(())
    }
    

    Redirect

    In the past we introduced the redirect submodule which provided redirect endpoints. And Response::redirect* which provided methods to create redirects inside endpoints. In this patch we're removing those APIs in favor of tide::Redirect which can be used both to create new redirect endpoint, and be used inside existing endpoints to redirect:

    use tide::Redirect;
    
    #[async_std::main]
    async fn main() -> tide::Result<()> {
        let mut app = tide::new();
        app.at("/fish").get(|_| async move { Ok("yum") });
    
        // Either create a redirect endpoint directly.
        app.at("/chashu").get(Redirect::new("/fish"));
    
        // Or redirect from inside an existing endpoint
        // enabling conditional redirects.
        app.at("/nori").get(|_| async move { Redirect::new("/fish") });
    
        app.listen("127.0.0.1:8080").await?;
        Ok(())
    }
    

    Big thanks to @ethanboxx for introducing this pattern to Tide. We'll be looking to introduce this pattern to more APIs in the future.

    Added

    • Added Response::take_body https://github.com/http-rs/tide/pull/499
    • Added Response::remove_header https://github.com/http-rs/tide/pull/508
    • Added Server::respond https://github.com/http-rs/tide/pull/503
    • Added @tirr-c as a co-author in Cargo.toml https://github.com/http-rs/tide/pull/507
    • Added Response::from_res https://github.com/http-rs/tide/pull/466
    • Added AsRef/AsMut impls to bridge Tide and http-types's req/res types https://github.com/http-rs/tide/pull/510
    • Added Into<http_types::Request> for Request https://github.com/http-rs/tide/pull/510
    • Added Response::header https://github.com/http-rs/tide/pull/515
    • Added tide::log::start which starts a logger that pretty-prints in development and prints ndjson in release mode https://github.com/http-rs/tide/pull/495

    Removed

    • Removed http-service in favor of Server::respond https://github.com/http-rs/tide/pull/503
    • Removed the redirects directory in favor of tide::Redirect https://github.com/http-rs/tide/pull/513

    Changed

    • Unified all redirect functionality into a single Redirect type https://github.com/http-rs/tide/pull/513
    • The log middleware now logs time more accurately https://github.com/http-rs/tide/pull/517

    Internal

    • Apply clippy https://github.com/http-rs/tide/pull/526
    • Bump version number in README https://github.com/http-rs/tide/pull/489
    • Changed file structure to match external exports https://github.com/http-rs/tide/pull/502
    • Remove unused dependencies https://github.com/http-rs/tide/pull/506
    • Added a no-dev-deps check to CI https://github.com/http-rs/tide/pull/512
    • Fibonnacci example now uses Instant::elapsed https://github.com/http-rs/tide/pull/522
    Source code(tar.gz)
    Source code(zip)
  • v0.8.1(May 7, 2020)

    docs.rs

    This patch contains several bug fixes and doc tweaks.

    Added

    • Route::serve_dir now applies some degree of mime guessing https://github.com/http-rs/tide/pull/461
    • Added the "Tide Channels" post to the README https://github.com/http-rs/tide/pull/464
    • Added documentation on Tide's default backend https://github.com/http-rs/tide/pull/467

    Changed

    • Updated the crate version in the README https://github.com/http-rs/tide/pull/475

    Fixed

    • Check directory traversal attack without file system visit https://github.com/http-rs/tide/pull/473
    • Correctly parse multiple values from the Cookie header https://github.com/http-rs/tide/pull/484
    Source code(tar.gz)
    Source code(zip)
  • v0.8.0(Apr 24, 2020)

    API Documentation

    This patch introduces the use of the ? operator in Endpoints, initial support for Server-Sent Events, static file serving, and a new submodule hierarchy. This continues the integration of http-types we started in v0.7.0

    Fallible endpoints

    Tide now allows the use of ? in endpoints. Errors are automatically converted to tide::Error and have a status code of 500 assigned to them. Overriding status codes can be done through the use of the tide::Status trait which is included in the prelude.

    use async_std::{fs, io};
    use tide::{Response, StatusCode};
    
    #[async_std::main]
    async fn main() -> io::Result<()> {
        let mut app = tide::new();
    
        app.at("/").get(|_| async move {
            let mut res = Response::new(StatusCode::Ok);
            res.set_body(fs::read("my_file").await?);
            Ok(res)
        });
    
        app.listen("localhost:8080").await?;
        Ok(())
    }
    

    Server-Sent Events

    This release makes the first steps towards integrating channels in Tide. In this release we're introducing support for Server-Sent Events, unidirectional event streams that operate over HTTP. This can be used for implementing features such as live-reloading, or sending notifications.

    use tide::sse;
    
    #[async_std::main]
    async fn main() -> Result<(), std::io::Error> {
        let mut app = tide::new();
        app.at("/sse").get(sse::endpoint(|_req, sender| async move {
            sender.send("fruit", "banana", None).await;
            sender.send("fruit", "apple", None).await;
            Ok(())
        }));
        app.listen("localhost:8080").await?;
        Ok(())
    }
    

    Connecting to the stream can be done using async-sse or from the browser:

    var sse = new EventSource('/sse');
    sse.on("message", (ev) => console.log(ev));
    

    In the future we may expand on these APIs to allow other endpoints to send messages on established channels, but in order to do so we need to implement session management first.

    Static file serving

    Tide is now able to serve static directories through the Route::serve_dir method. This allows mapping URLs to directories on disk:

    #[async_std::main]
    async fn main() -> Result<(), std::io::Error> {
        let mut app = tide::new();
        app.at("/public/images").serve_dir("images/")?;
        app.listen("127.0.0.1:8080").await?;
        Ok(())
    }
    

    Revamped Hierarchy

    Tide has been changing a lot recently, and we've made changes to our submodules so we can keep up. We no longer have a singular middleware submodule and instead split them up by topic. This will allow us to continue to expand on Tide's capabilities, while keeping things easy to find.

    Screenshot_2020-04-24 tide - Rust

    Future Directions

    The next step for us is to continue to integrate http-types into Tide, focusing on the Request, Response, and Body types. This will likely coincide with a [email protected] release. After that our goal is to expand our capabilities around file serving and testing. And finally adding support for WebSockets, sessions, and TLS.

    We're excited for the future of Tide, and we're glad you're here with us!

    Added

    • Enabled the use of ? in Endpoint https://github.com/http-rs/tide/pull/438
    • HttpService is now directly implemented on Server https://github.com/http-rs/tide/pull/442
    • Added Route::serve_dir which can serve full directories https://github.com/http-rs/tide/pull/415
    • Added a "getting started" section to the README https://github.com/http-rs/tide/pull/445
    • Added Response::redirect_permanent https://github.com/http-rs/tide/pull/435
    • Added Response::redirect_temporary https://github.com/http-rs/tide/pull/435
    • Added the redirect submodule https://github.com/http-rs/tide/pull/450
    • Added redirect::permanent https://github.com/http-rs/tide/pull/435
    • Added the log submodule providing structured logging https://github.com/http-rs/tide/pull/451
    • Added a test for chunked encoding https://github.com/http-rs/tide/pull/437
    • Added a logo and favicon for rustdoc https://github.com/http-rs/tide/pull/459
    • Added the sse submodule providing initial Server-Sent Events support https://github.com/http-rs/tide/pull/456
    • Added tide::{Body, StatusCode}, re-exported from http-types https://github.com/http-rs/tide/pull/455
    • Added instructions to the README how to run examples https://github.com/http-rs/tide/pull/460
    • Added an example for a nesting tide server https://github.com/http-rs/tide/pull/429

    Changed

    • All Endpoints now return Result<Into<Response>> https://github.com/http-rs/tide/pull/438
    • Renamed tide::redirect to tide::redirect::temporary https://github.com/http-rs/tide/pull/450
    • Logging middleware is now enabled by default https://github.com/http-rs/tide/pull/451
    • Moved CORS middleware to the security submodule https://github.com/http-rs/tide/pull/453
    • Allow tide::Result<Response> to be abbreviated as tide::Result https://github.com/http-rs/tide/pull/457
    • Replaced the use of IntoResponse with Into<Response> https://github.com/http-rs/tide/pull/463

    Removed

    • Removed unused server impl code https://github.com/http-rs/tide/pull/441
    • Removed the tide::server::Service struct https://github.com/http-rs/tide/pull/442
    • Removed the tide::server submodule https://github.com/http-rs/tide/pull/442
    • Removed the tide::middleware submodule https://github.com/http-rs/tide/pull/453
    • Removed the IntoResponse trait https://github.com/http-rs/tide/pull/463

    Fixed

    • Fixed CORS behavior if there is no origin header https://github.com/http-rs/tide/pull/439
    • Fixed documentation formatting in lib.rs https://github.com/http-rs/tide/pull/454
    • Fixed a stability issue in our tests regarding by opening random ports https://github.com/http-rs/tide/pull/446
    Source code(tar.gz)
    Source code(zip)
  • v0.7.0(Apr 17, 2020)

    This patch switches Tide to use http-types for its interface, and async-h1 as its default engine. Additionally we now allow middleware to be defined on a per-route basic. Put together this is effectively an overhaul of Tide's internals, and constitutes a fairly large change.

    If you're using Tide in production please be advised this release may have a different stability profile than what you've become used to in the past, and we advice upgrading with appropriate caution. If you find any critical bugs, filing a bug report on one of the issue trackers and reaching out directly over Discord is the quickest way to reach the team.

    The reason we're making these changes is that it will allow us to finally polish our error handling story, severely simplify internal logic, and enable many many other features we've been wanting to implement for years now. This is a big step for the project, and we're excited to be taking it together.

    Added

    • Enables per-route middleware https://github.com/http-rs/tide/pull/399
    • Added an example for chunked encoding https://github.com/http-rs/tide/pull/430

    Changed

    • Made Endpoint::call generic over the lifetime https://github.com/http-rs/tide/pull/397
    • Removed Result from Request::cookie https://github.com/http-rs/tide/pull/413
    • Use async-h1 as the default engine https://github.com/http-rs/tide/pull/414
    • Use http-types as the interface types https://github.com/http-rs/tide/pull/414

    Fixed

    • Fixed a path on the changelog.md https://github.com/http-rs/tide/pull/395
    • Removed unnecessary internal pin-project call https://github.com/http-rs/tide/pull/410
    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Jan 30, 2020)

    API Documentation

    This patch introduces a new cookies API, based on the excellent cookie crate. Working with cookies is a staple for any web server, and Tide's new API now makes this entirely declarative.

    Additionally we've added back CORS support. This makes it possible for possible to configure the single-origin policy of browsers, which is an incredibly valuable resource.

    And finally nesting services with Tide has become even easier. Building on the APIs in 0.5.0, the manual song-and-dance required to nest APIs is no longer required, and services can now be nested as-is through the Route::nest API.

    Examples

    Cookies

    use cookie::Cookie;
    use tide::Response;
    
    let mut app = tide::new();
    
    app.at("/").get(|req| async move {
        println!("cat snack: {:?}", req.cookie("snack"));
        Response::new(200)
    });
    app.at("/set").get(|req| async move {
        let mut res = Response::new(200);
        res.set_cookie(Cookie::new("snack", "tuna"));
        res
    });
    app.listen("127.0.0.1:8080").await?;
    

    CORS

    Make GET, POST, and OPTIONS endpoints on this server accessible from any web page.

    use http::header::HeaderValue;
    use tide::middleware::{Cors, Origin};
    
    let rules = Cors::new()
        .allow_methods(HeaderValue::from_static("GET, POST, OPTIONS"))
        .allow_origin(Origin::from("*"))
        .allow_credentials(false);
    
    let mut app = tide::new();
    app.middleware(rules);
    app.at("/").post(|_| async { Response::new(200) });
    app.listen("localhost:8080").await?;
    

    Nesting

    Nest the inner serve inside the outer service, exposing GET /cat/nori.

    let mut inner = tide::new();
    inner.at("/nori").get(|_| async { Response::new(200) });
    
    let mut outer = tide::new();
    outer.at("/cat").nest(inner);
    
    outer.listen("localhost:8080").await?;
    

    Added

    • Added Route::all to match all HTTP methods on a route (#379)
    • Added Route::nest to nest instances of tide::Server on sub-routes (#379)
    • Added a new cors submodule containing CORS control middleware (#373)
    • Added Request::cookie to get a cookie sent by the client (#380)
    • Added Response::set_cookie to instruct the client to set a cookie (#380)
    • Added Response::remove_cookie to instruct the client to unset a cookie (#380)

    Changed

    • Changed the behavior of optional params in Request.query to be more intuitive (384)
    • Improved the debugging experience of query deserialization errors (384)
    • Updated the GraphQL example to use the latest version of Juniper (#372)
    • Tide no longer prints to stdout when started (387)

    Fixed

    • Fixed an incorrect MIME type definition on Response::body (378)
    Source code(tar.gz)
    Source code(zip)
  • 0.5.1(Dec 20, 2019)

  • 0.5.0(Dec 20, 2019)

    API Documentation

    This release introduces the ability to nest applications, add logging middleware, and improves our documentation.

    Nesting applications is a useful technique that can be used to create several sub-applications. This allows creating clear points of isolation in applications that can be used completely independently of the main application. But can be recombined into a single binary if required.

    Being able to nest applications is also a necessary first step to re-introduce per-route middleware, which we'll do in subsequent patches.

    Examples

    let mut inner = tide::new();
    inner.at("/").get(|_| async { "root" });
    inner.at("/foo").get(|_| async { "foo" });
    inner.at("/bar").get(|_| async { "bar" });
    
    let mut outer = tide::new();
    outer
        .at("/nested")
        .strip_prefix() // catch /nested and /nested/*
        .get(inner.into_http_service()); // the prefix /nested will be stripped here
    

    Added

    • Added Route::strip_prefix (#364)
    • Added the ability Services to be nested (#364)
    • Added middleware::RequestLogger (#367)

    Changed

    • Updated and improved the documentation (#363)
    Source code(tar.gz)
    Source code(zip)
  • 0.4.0(Nov 27, 2019)

    This release is a further polishing of Tide's APIs, and works towards significantly improving Tide's user experience. The biggest question left unanswered after this patch is how we want to do error handling, but aside from that the end-user API should be pretty close to where we want it to be.

    The biggest changes in this patch is endpoints now take Request instead of Context. The new Request and Response types are no longer type aliases but concrete types, making them substantially easier to use. This also means that we've been able to fold in all the Ext methods we were exposing, enabling methods such as let values: Schema = req.body_json()?; to deserialize an incoming JSON body through a Serde schema. This should make it significantly easier to write APIs with Tide out of the box.

    Example

    Create a "hello world" app:

    #[async_std::main]
    async fn main() -> Result<(), std::io::Error> {
        let mut app = tide::new();
        app.at("/").get(|_| async move { "Hello, world!" });
        app.listen("127.0.0.1:8080").await?;
        Ok(())
    }
    

    Redirect from /nori to /chashu:

    #[async_std::main]
    async fn main() -> Result<(), std::io::Error> {
        let mut app = tide::new();
        app.at("/chashu").get(|_| async move { "meow" });
        app.at("/nori").get(tide::redirect("/chashu"));
        app.listen("127.0.0.1:8080").await?;
        Ok(())
    }
    

    Added

    • Added logger::RequestLogger based on log (replaces logger:RootLogger).
    • Added Request with inherent methods (replaces Context).
    • Added Server (replaces App).
    • Added Response (replacing a type alias of the same name).
    • Added a prelude submodule, holding all public traits.
    • Added a new free function, a shorthand for Server::new.
    • Added a with_state free function, a shorthand for Server::with_state.
    • Added Result type alias (replaces EndpointResult).
    • Added a redirect free function to redirect from one endpoint to another.

    Changed

    • Resolved an #[allow(unused_mut)] workaround.
    • Renamed ExtractForms to ContextExt.
    • Response is now a newly defined type.

    Removed

    • Removed logger::RootLogger (replaced by logger:RequestLogger).
    • Removed internal use of the box_async macro.
    • Removed Context (replaced by Request).
    • Removed the Response type alias (replaced by a new Response struct).
    • Removed App (replaced by Server).
    • Temporarily disabled the multipart family of APIs, improving compilation speed by ~30%.
    • Removed EndpointResult (replaced by Result).
    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Oct 31, 2019)

    Documentation link

    This is the first release in almost 6 months; introducing a snapshot of where we were right before splitting up the crate. This release is mostly similar to 0.2.0, but sets us up to start rewinding prior work on top.

    Added

    • Added "unstable" feature flag.
    • Added example for serving static files.
    • Added keywords and categories to Cargo.toml.
    • Implemented Default for App.
    • Added App::with_state constructor method.
    • Added Context::state (replacing Context::app_data)

    Changed

    • Fixed multipart uploads.
    • Fixed some doc tests.
    • Rename cookies::CookiesExt to cookies::ContextExt.
    • Rename querystring::ExtractQuery to querystring::ContextExt.
    • Switched CI provider from Travis to GitHub actions.
    • Updated README.
    • Updated all dependencies.
    • Replaced AppData with State.

    Removed

    • Removed the RFCs subdirectory.
    • Removed an extra incoming license requirement.
    • Removed outdated version logs.
    • Removed rustfmt.toml.
    • Removed Context::app_data (replaced with Context::state).
    Source code(tar.gz)
    Source code(zip)
Owner
http-rs
Fast, friendly, asynchronous HTTP in Rust
http-rs
xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance.

xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance

Mohamed Daahir 3.4k Jan 6, 2023
HTTPie: human-friendly CLI HTTP client for the API era

HTTPie: human-friendly CLI HTTP client for the API era HTTPie (pronounced aitch-tee-tee-pie) is a command-line HTTP client. Its goal is to make CLI in

null 25.4k Dec 30, 2022
rh: user-friendly command-line HTTP client

Rust HTTP Cli The command name in your terminal is rh. rh: user-friendly command-line HTTP client rh is a user-friendly, lightweight and performant co

null 8 Nov 30, 2022
Pyre - A fast python HTTP server inspired by japronto written in rust.

Pyre - A fast python HTTP server inspired by japronto written in rust.

null 135 Nov 26, 2022
ratpack: a simpleton's HTTP framework (for rust-lang)

ratpack: a simpleton's HTTP framework (for rust-lang) ratpack is idealized in the simplicity of the sinatra (ruby) framework in its goal, and attempts

ZeroTier, Inc. 5 Jun 29, 2022
An easy and powerful Rust HTTP Client

reqwest An ergonomic, batteries-included HTTP Client for Rust. Plain bodies, JSON, urlencoded, multipart Customizable redirect policy HTTP Proxies HTT

Sean McArthur 6.8k Dec 31, 2022
Minimal Rust HTTP client for both native and WASM

ehttp: a minimal Rust HTTP client for both native and WASM If you want to do HTTP requests and are targetting both native and web (WASM), then this is

Emil Ernerfeldt 105 Dec 25, 2022
An HTTP library for Rust

hyper A fast and correct HTTP implementation for Rust. HTTP/1 and HTTP/2 Asynchronous design Leading in performance Tested and correct Extensive produ

null 11k Jan 8, 2023
FeignHttp is a declarative HTTP client. Based on rust macros.

FeignHttp is a declarative HTTP client. Based on rust macros. Features Easy to use Asynchronous request Configurable timeout settings Suppor

null 46 Nov 30, 2022
Pretend is a macros-based declarative Rust HTTP client

pretend is a modular, Feign-inspired HTTP, client based on macros. It's goal is to decouple the definition of a REST API from it's implementation.

null 24 Aug 3, 2022
A backend providing a HTTP REST like interface for uploading files written in rust.

UploadServer A backend providing a HTTP REST like interface for uploading files written in rust. API Documentation License This project is licensed un

null 4 Nov 20, 2022
🐱‍👤 Drop-in HTTP replacement module for Garry's Mod

??‍?? gmsv_reqwest This module is a drop-in replacement for Garry's Mod's HTTP function, inspired by gmsv_chttp created by timschumi. The module uses

William 38 Dec 12, 2022
Multi-stream HTTP downloader using range requests

chooch - An Amazing Project Downloads files faster than wget/curl (in theory) using multiple connections. Chooch recycles the slowest connection and r

null 13 Sep 14, 2022
A GraphQL server library implemented in Rust

A GraphQL server library implemented in Rust Async-graphql is a high-performance server-side library that supports all GraphQL specifications. Feature

null 2.6k Jan 8, 2023
Typed, correct GraphQL requests and responses in Rust

graphql_client A typed GraphQL client library for Rust. Features Precise types for query variables and responses. Supports GraphQL fragments, objects,

GraphQL Rust 914 Dec 27, 2022
🕵️Scrape multiple media providers on a cron job and dispatch webhooks when changes are detected.

Jiu is a multi-threaded media scraper capable of juggling thousands of endpoints from different providers with unique restrictions/requirements.

Xetera 47 Dec 6, 2022
Rust bindings to libcurl

curl-rust libcurl bindings for Rust Quick Start use std::io::{stdout, Write}; use curl::easy::Easy; // Print a web page onto stdout fn main() {

Alex Crichton 882 Dec 31, 2022
A rule34 scraper made in rust this time

rust-34-scraper A rule34 scraper made in rust this time Building Clone the repository Execute cargo build --release --release is an optimized build pr

null 3 Jun 10, 2022
An experimental HTTP server in Rust that supports HTTP/1.1, HTTP/2, and HTTP/3 over QUIC.

?? H123 An experimental HTTP server in Rust that supports HTTP/1.1, HTTP/2, and HTTP/3 over QUIC. Warning This is an experimental project and not inte

Naoki Ikeguchi 7 Dec 15, 2022
Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

null 294 Dec 23, 2022