Skip to content

design doc #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
yoshuawuyts opened this issue Jul 7, 2019 · 3 comments
Closed

design doc #1

yoshuawuyts opened this issue Jul 7, 2019 · 3 comments

Comments

@yoshuawuyts
Copy link
Member

yoshuawuyts commented Jul 7, 2019

Surf Architecture

This is a (quick) design document for Surf, an HTTP client with middleware
support in Rust.

Goals

  • concise API for most common cases
  • builder API for more complex cases
  • backend agnostic (e.g. swap Hyper for libcurl or browser fetch)
  • extendable using middleware
    • enable some middleware by default (compression, redirects, etc)
    • provide a way to opt-out of default middleware

API Example

// text
let res = xhr::get("/endpoint").send().await?;
dbg!(res);

// json
#[derive(Deserialize)]
struct User { name: String }
let user: User = xhr::get("/endpoint").send_json().await?;
dbg!(user);

Rust types are nice in that send_json can take a type parameter (User in
this case) which allows it to try and deserialize the type into that struct, and
if it can't it'll return an error. This allows for super concise JSON APIs!

Another implication of the way this is done is that the struct definitions can
be shared between servers & clients. This means that we can guarantee a server
and corresponding client can work with each other, even if the wire protocol is
something like JSON. And this can further be enhance using something like
session-types.

Architecture

  • surf as the top-level framework crate with sane defaults & middleware
  • http-client to provide the swappable backend layer
    • the middleware layer probably should live here too
    • contains types also
  • a collection of useful middleware in the same repo as surf
 -----------------
| Surf middleware | = `cargo add surf`
| Surf core       |
 -----------------
       ^
       |-----------------
                        |
 -------------------    |
| http-client-curl  |   |
| http-client-fetch |   |
 -------------------    |
| http-client-hyper | --|
| http-client       | = `cargo add http-client`
 -------------------

Middleware

let user: User = xhr::get("/endpoint")
    .middleware(some_middleware::new("value"))
    .send_json().await?;

We should take a look at make-fetch-happen and start by building out the features it implements. We should probably define some sort of prioritization here (:

Connection Pooling

A core feature of surf should be the ability to perform connection pooling.
I'm not sure yet how that works, as I've never used & researched it. But it
seems very important, and we should come up with a design here. I'm thinking
in terms of API it may be something like this:

let pool = surf::Pool::new()
    .max_sockets(512)
    .max_free_sockets(256)
    .build();

let res = pool.get("some_url").send().await?;

I somehow suspect that a similar API may be relevant for performing H2 requests
down the line too, as it's yet another form of multiplexing. It's a bit early
right now, but if we can get the API right we can add this functionality at a
later stage without too many surface changes.

Conclusion

This is about it for now. I hope I covered the rough outlines here!

References

@yoshuawuyts
Copy link
Member Author

yoshuawuyts commented Jul 7, 2019

Oh, ideally we could make this work with runtime too! Here's an example of how google's fuschia project swapped out the connection layer in Hyper for their OS. We did something similar for http-service recently too.

@zkat
Copy link
Collaborator

zkat commented Jul 7, 2019

This looks great, btw, including the runtime support!

We should take a look at make-fetch-happen and start by building out the features it implements. We should probably define some sort of prioritization here (:

For me, the priority looks something like:

  1. Connection pooling - the performance difference between pooled and unpooled connections is incredible and I think this should be fairly high priority.
  2. http-compliant caching with a pluggable backend (I want to be able to use cacache but it should be bound to that.) There's already a protocol for this we could yank straight from fetch, and make-fetch-happen implements it
  3. http-compliant request retries. This'll need to be configurable, and it's only possible in a few specific scenarios
  4. Proxies - though honestly this is very low on the list because I don't think it's going to be necessary for dstopic.

@yoshuawuyts
Copy link
Member Author

I think we've done a reasonable job at implementing the general design. I think it's okay to go ahead and close this (:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants