Replies: 6 comments 19 replies
-
We should also add some form of id and version here, so we can skip the message in the agent if it doesn't match on version.
+1 to version negotiation. Should the protocol support encryption?
I'm still not sure we should roll our own here, were we not exploring using existing stuff? What happened? |
Beta Was this translation helpful? Give feedback.
-
Trying to understand the requirements:
|
Beta Was this translation helpful? Give feedback.
-
This change became real necessity after #1731 and we already have quite a lot of protocol changes we want to push and are unable at the moment. Protocol Negotation
API Level
I'll try to draft pseudo code for how I'd imagine it to work. |
Beta Was this translation helpful? Give feedback.
-
What about attaching an HTTP engine to each part of mirrord, and communicating REST style? We could make the messages be more dynamic, use only what I'm thinking something like: #[post("/v1/layer_read/{file}")]
fn layer_read(file: FileId, message: Json<JsonObject>) -> Response {
let bytes = message.get("bytes");
let amount_read = message.get("amount_read");
....
}
#[post("/v100/layer_read/{file}")]
fn layer_read(file: FileId, message: Json<JsonObject>) -> Response {
// we have changed the name `bytes` to `buffer`
let buffer = message.get("buffer");
// we removed the `amount_read` field, now we just rely on the length
let amount_read = buffer.len();
...
} #[post("/v1/agent_read/{file}")]
fn agent_read(file: FileId) -> Response {
let read = FileManager::read(file);
let read_response = json!({ "bytes": read.bytes, "amount_read": read.bytes.len() });
...
read_response
}
#[post("/v100/agent_read/{file}")]
fn agent_read(file: FileId) -> Response {
let read = FileManager::read(file);
let read_response = json!({ "bytes": read.bytes });
...
read_response
} We could support version negotiation as a first message exchange between layer/operator/agent. How awful does this sound? |
Beta Was this translation helpful? Give feedback.
-
One Option, though a bit large at first, will allow a greater deal of control is creating a completely RPC-based API for the agent. With Cap'n Proto I created a small example of how it can look like for our stealing of tcp interface ByteStream {
write @1 (bytes: Data);
close @2 ();
expectSize @3 (lower: UInt64, upper: UInt64);
}
interface HttpRequest {
struct Header {
name @0 :Text;
value @1 :Text;
}
struct Head {
method @0 :Text;
uri @1 :Text;
headers @2 :List(Header);
version: @3 :Text;
}
head @0 (header: Head);
body @1 (bytes: ByteStream);
}
interface HttpResponse {
}
interface Tcp {
struct ConnectionInfo {
connectionId @0 :UInt64;
remoteAddress @1 :Text; # IP?
destinationPort @2 :UInt16;
sourcePort @3 :UInt16
localAddress @4 :Text; # IP?
}
struct HttpConnectionInfo {
connectionId @0 :UInt64;
requestId @1 :UInt64;
port: @2 :UInt16;
}
connection @0 (connection_info: ConnectionInfo, inbound: ByteStream) -> (outbound: ByteStream);
http @1 (connection_info: HttpConnectionInfo, request: HttpRequest) -> (response: HttpResponse);
}
interface AgentTcp {
struct Steal {
port @0 :UInt16;
type union {
full @1 :Void;
header @2 :Text;
path @3 :Text;
}
}
mirror @0 (port: UInt16) -> (tcp: Tcp);
steal @0 (type: Steal) -> (tcp: Tcp);
} Or it can be split it to separate full port or HTTP only steals where the value interface ByteStream {
write @1 (bytes: Data);
close @2 ();
expectSize @3 (lower: UInt64, upper: UInt64);
}
interface HttpRequest {
struct Header {
name @0 :Text;
value @1 :Text;
}
struct Head {
method @0 :Text;
uri @1 :Text;
headers @2 :List(Header);
version: @3 :Text;
}
head @0 (header: Head);
body @1 (bytes: ByteStream);
}
interface HttpResponse {
}
interface Tcp {
struct ConnectionInfo {
connectionId @0 :UInt64;
remoteAddress @1 :Text; # IP?
destinationPort @2 :UInt16;
sourcePort @3 :UInt16
localAddress @4 :Text; # IP?
}
connection @0 (connection_info: ConnectionInfo, inbound: ByteStream) -> (outbound: ByteStream);
}
interface Http {
struct HttpConnectionInfo {
connectionId @0 :UInt64;
requestId @1 :UInt64;
port: @2 :UInt16;
}
request @0 (connection_info: HttpConnectionInfo, request: HttpRequest) -> (response: HttpResponse);
}
interface AgentTcp {
struct HttpSteal {
port @0 :UInt16;
type union {
header @2 :Text;
path @3 :Text;
}
}
mirror @0 (port: UInt16) -> (tcp: Tcp);
steal @1 (port: UInt16) -> (tcp: Tcp);
httpSteal @2 (type: HttpSteal) -> (http: Http);
} The move can be partial with a large union but it is also a lot of work. But the main idea is I want to reuse the request handlers like the Tcp One for both steal and mirror. |
Beta Was this translation helpful? Give feedback.
-
We should make progress with the protocol (#1353 #1354 #1355 #1356), as we have already few stuff pending protocol breakage and we don't want to do it without atleast featuring forward-proof changes. i.e if we break now let's make this the last break.
I'd say a good start would add a framing (size of each message before message) to our codec, so even if parsing fails because version is broken, we can skip it in the operator or even agent.
This also might improve performance since we can know how much data needs to be read before parsing, as now we have a slow cycle where we keep reading and going back to bincode parse.
Secondly, I'd add that the initial message would be each side sending their version, so the other side can adjust what it can send to it - so atleast for now we'll have a global variable saying "VERSION = 1" then we can decide if to send the new type of message or not, until we build a proper abstraction.
Lastly, I guess we should start with "tons of boiler plate" code that will just work, then try to figure out how to macro/do magic to make it short and ergonomic. Maybe introduce a new crate for defining network protocols (I suggest calling it
nobuf
).In short, we'd want the protocol to be multiplex (reqid,responseid, response being optional so we don't block on it) and with nice Rust abstractions (similar to tower probably but different ofc).
I'd love to hear any thoughts on the mind dump I had here - this is a "tough" thing to build correctly, but we need to do it atleast once :)
Beta Was this translation helpful? Give feedback.
All reactions