From 0c5dda057eaa4f202e780befa78e5ee6a9987ae6 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Thu, 14 May 2020 23:19:26 +0200 Subject: [PATCH] Message order flip --- draft-fabaccess-protocol.md | 42 +++++++++++++++++++++++++++ src/connection.rs | 58 ++++++++++++++++++++----------------- 2 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 draft-fabaccess-protocol.md diff --git a/draft-fabaccess-protocol.md b/draft-fabaccess-protocol.md new file mode 100644 index 0000000..c30d76b --- /dev/null +++ b/draft-fabaccess-protocol.md @@ -0,0 +1,42 @@ +# Stream initiation + +In a session there are two parties: The initiating entity and the receiving +entity. This terminology does not refer to information flow but rather to the +side opening a connection respectively the one listening for connection +attempts. +In the currently envisioned use-case the initiating entity is a) a client +(i.e. interactive or batch/automated program) trying to interact in some way or +other with a server b) a server trying to exchange / request information +with/from another server (i.e. federating). The receiving entity however is +already a server. + +Additionally the amount and type of clients is likely to be more diverse and +less up to date than the servers. +Conclusions I draw from this: + - Clients are more likely to implement an outdated version of the communication + protocol. + - The place for backwards-compatability should be the servers. + - Thus the client (initiating entity) should send the expected API version + first, the server then using that as a basis to decide with which API + version to answer. + +# Stream negotiation + +Since the receiving entity for a connection is responsible for the machines it +controls it imposes conditions for connecting either as client or as federating +server. At least every initiating entity is required to authenticate itself to +the receiving entity before attempting further actions or requesting +information. But a receiving entity can require other features, such as +transport layer encryption. +To this end a receiving entity informs the initiating entity about features that +it requires from the initiating entity before taking any further action and +features that are voluntary to negotiate but may improve qualities of the stream +(such as message compression) + +A varying set of conditions implies negotiation needs to take place. Since +features potentially require a strict order (e.g. Encryption before +Authentication) negotiation has to be a multi-stage process. Further +restrictions are imposed because some features may only be offered after others +have been established (e.g. SASL authentication only becoming available after +encryption, EXTERNAL mechanism only being available to local sockets or +connections providing a certificate) diff --git a/src/connection.rs b/src/connection.rs index 14cd4b5..b99c355 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -15,38 +15,42 @@ pub async fn handle_connection(log: Logger, mut stream: TcpStream) -> Result<()> let program = "Difluoroborane-0.1.0"; let version = (0u32,1u32); - let mut message = capnp::message::Builder::new_default(); - let greet_outer = message.init_root::(); - let mut greeting = greet_outer.init_greet(); - greeting.set_host(host); - greeting.set_program(program); - greeting.set_major(version.0); - greeting.set_minor(version.1); - capnp_futures::serialize::write_message(&mut stream, message).await?; + { + let receive_options = capnp::message::ReaderOptions::default(); + let message = capnp_futures::serialize::read_message(&mut stream, receive_options).await.unwrap().unwrap(); + let m = message.get_root::().unwrap(); - stream.flush().await?; - - let receive_options = capnp::message::ReaderOptions::default(); - let message = capnp_futures::serialize::read_message(&mut stream, receive_options).await.unwrap().unwrap(); - let body: capnp::any_pointer::Reader = message.get_root().unwrap(); - let m = body.get_as::().unwrap(); - - if m.has_greet() { - match m.which() { - Ok(gen::message::Which::Greet(Ok(r))) => { - println!("Host {} with program {} is saying hello. They speak API version {}.{}.", - r.get_host().unwrap(), - r.get_program().unwrap(), - r.get_major(), - r.get_minor()) - }, - _ => { - // We *JUST* checked that it's a greeting. This can not happen - unreachable!() + if m.has_greet() { + match m.which() { + Ok(gen::message::Which::Greet(Ok(r))) => { + println!("Host {} with program {} is saying hello. They speak API version {}.{}.", + r.get_host().unwrap(), + r.get_program().unwrap(), + r.get_major(), + r.get_minor()) + }, + _ => { + // We *JUST* checked that it's a greeting. This can not happen + unreachable!() + } } } } + { + let mut message = capnp::message::Builder::new_default(); + let greet_outer = message.init_root::(); + let mut greeting = greet_outer.init_greet(); + greeting.set_host(host); + greeting.set_program(program); + greeting.set_major(version.0); + greeting.set_minor(version.1); + + capnp_futures::serialize::write_message(&mut stream, message).await?; + } + + stream.flush().await?; + Ok(()) }