rustls / rustls-ffi

Use Rustls from any language
Other
132 stars 30 forks source link

Factor out shared Session code #37

Closed jsha closed 3 years ago

jsha commented 3 years ago

In rustls, ClientSession and ServerSession both implement the Session trait, which contains a number of shared methods. The C bindings for those methods are duplicated between client.rs and server.rs. We explored methods of de-duplicating them in #30, and concluded that:

jsha commented 3 years ago

I have an idea for how to do this in a more satisfactory way that works at the API level, not just internally. There are actually very few methods that are specific to client or server. So we should define the API mainly in terms of the commonality: have one type returned from both the client and server constructors, and define both the shared and the distinct functionality on that type. If a client-specific function is called on something that's really a server connection, return an error.

I'll describe these in terms of the unreleased connection naming in the rustls API:

struct Conn {
  conn: Box<mut dyn Any> // Holds a ClientConnection or ServerConnection
  userdata: *mut c_void
}

impl CastPtr for rustls_connection {
  RustType = Conn
}
struct rustls_connection *rustls_client_connection_new();
struct rustls_connection *rustls_server_connection_new();

// Each of these does a `downcast_mut` to ServerConnection or ClientConnection
rustls_connection_read_tls(struct rustls_connection *conn, ...);
rustls_connection_write_tls(struct rustls_connection *conn, ...);
rustls_connection_read(struct rustls_connection *conn, ...);
rustls_connection_write(struct rustls_connection *conn, ...);

// Returns false if the conn is actually a server connection
bool rustls_client_connection_is_early_data_accepted(struct rustls_connection *conn);
// Returns error if conn is actually a server connection
rustls_result rustls_client_connection_early_data_write(struct rustls_connection *conn, buf, len);

// Stores an empty string if conn is actually a client connection
rustls_server_connection_get_sni_hostname(struct rustls_connection *conn, buf, len);
icing commented 3 years ago

Nice!

Just learned that dyn Trait holds 2 pointers internally, one of the instance and one trait function table ref. And that there is more special sauce in dyn Any. Hmm, and then I read something like:

Note that &dyn Any is limited to testing whether a value is of a specified concrete type, and cannot be used to test whether a type implements a trait.

Which means that the implementations of dyn Trait and dyn Any are internally radically different and that the one has really no relation to the other. And Any is a trait, but dyn Any is not about a Trait - it just looks like it.

I guess this is an example of Rust evolution. But it is tricky for a newcomer to learn that dyn Trait Any is a trait and dyn Any are in a tangled relationship and limited by their internal implementations.

djc commented 3 years ago

Why not use dyn Connection anyway here?

jsha commented 3 years ago

dyn Connection was my original plan but AFAICT I can't downcast a dyn Connection to ServerConnection or ClientConnection, which is needed to call the methods that are specific to one or the other. If I'm wrong about that, that'd be great!