docs: handshake documentation
This commit is contained in:
parent
eb8410718d
commit
c61b6021db
5 changed files with 28 additions and 3 deletions
|
|
@ -7,15 +7,20 @@ use crate::reader::backend::BackendProtoReader;
|
||||||
use crate::writer::frontend::FrontendProtoWriter;
|
use crate::writer::frontend::FrontendProtoWriter;
|
||||||
use crate::writer::protowriter::ProtoFlush;
|
use crate::writer::protowriter::ProtoFlush;
|
||||||
|
|
||||||
|
/// Performs client-side handshake with the server until the `ReadyForQuery` message is received.
|
||||||
|
/// For more info visit the [`55.2.1. Start-up`](https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-START-UP)
|
||||||
pub async fn do_client_handshake(
|
pub async fn do_client_handshake(
|
||||||
writer: &mut (impl FrontendProtoWriter + ProtoFlush),
|
writer: &mut (impl FrontendProtoWriter + ProtoFlush),
|
||||||
reader: &mut impl BackendProtoReader,
|
reader: &mut impl BackendProtoReader,
|
||||||
request: HandshakeRequest,
|
request: HandshakeRequest,
|
||||||
) -> Result<HandshakeResponse, ClientHandshakeError> {
|
) -> Result<HandshakeResponse, ClientHandshakeError> {
|
||||||
|
|
||||||
|
// Send StartupMessage without SSLRequest
|
||||||
let startup_message: StartupMessageData = request.into();
|
let startup_message: StartupMessageData = request.into();
|
||||||
writer.write_startup_message(startup_message).await?;
|
writer.write_startup_message(startup_message).await?;
|
||||||
writer.flush().await?;
|
writer.flush().await?;
|
||||||
|
|
||||||
|
// Wait for AuthenticationOk
|
||||||
let auth = reader.read_proto().await?;
|
let auth = reader.read_proto().await?;
|
||||||
if !matches!(
|
if !matches!(
|
||||||
auth,
|
auth,
|
||||||
|
|
@ -24,6 +29,7 @@ pub async fn do_client_handshake(
|
||||||
return Err(ClientHandshakeError::UnexpectedAuthResponse(auth));
|
return Err(ClientHandshakeError::UnexpectedAuthResponse(auth));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read server parameter messages until ReadyForQuery is received
|
||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let msg = reader.read_proto().await?;
|
let msg = reader.read_proto().await?;
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,14 @@ use crate::message::special::StartupMessageData;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct HandshakeRequest {
|
pub struct HandshakeRequest {
|
||||||
version: i32,
|
pub version: i32,
|
||||||
parameters: Vec<(PgString, PgString)>,
|
pub parameters: Vec<(PgString, PgString)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HandshakeRequest {
|
impl HandshakeRequest {
|
||||||
|
|
||||||
|
/// Creates a new `HandshakeRequest` with the specified version.
|
||||||
|
/// Expected `version` is 196608 (3.0).
|
||||||
pub fn new(version: i32) -> Self {
|
pub fn new(version: i32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
version,
|
version,
|
||||||
|
|
@ -15,6 +18,9 @@ impl HandshakeRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a parameter to the startup message.
|
||||||
|
/// Generally recognized names are `user`, `database`, `option` and `replication` but others can be used.
|
||||||
|
/// For more info visit [`StartupMessage`](https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-STARTUPMESSAGE)
|
||||||
pub fn parameter(mut self, key: &str, value: &str) -> Self {
|
pub fn parameter(mut self, key: &str, value: &str) -> Self {
|
||||||
self.parameters.push((key.into(), value.into()));
|
self.parameters.push((key.into(), value.into()));
|
||||||
self
|
self
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub struct HandshakeResponse {
|
||||||
impl HandshakeResponse {
|
impl HandshakeResponse {
|
||||||
pub fn new(name: &str, pid: i32, key: i32) -> Self {
|
pub fn new(name: &str, pid: i32, key: i32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
version: format!("16.0 ({name})", name = name),
|
version: format!("16.0 ({name})"),
|
||||||
process_id: pid,
|
process_id: pid,
|
||||||
secret_key: key,
|
secret_key: key,
|
||||||
}
|
}
|
||||||
|
|
@ -37,6 +37,7 @@ impl TryFrom<&[BackendMessage]> for HandshakeResponse {
|
||||||
process_id = Some(data.process);
|
process_id = Some(data.process);
|
||||||
secret_key = Some(data.secret);
|
secret_key = Some(data.secret);
|
||||||
}
|
}
|
||||||
|
// Different messages are ignored during the handshake
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,15 @@ use crate::reader::frontend::FrontendProtoReader;
|
||||||
use crate::writer::backend::BackendProtoWriter;
|
use crate::writer::backend::BackendProtoWriter;
|
||||||
use crate::writer::protowriter::ProtoFlush;
|
use crate::writer::protowriter::ProtoFlush;
|
||||||
|
|
||||||
|
/// Performs server-side handshake with the client until ending it with `ReadyForQuery` message.
|
||||||
|
/// For more info visit the [`55.2.1. Start-up`](https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-START-UP)
|
||||||
pub async fn do_server_handshake(
|
pub async fn do_server_handshake(
|
||||||
writer: &mut (impl BackendProtoWriter + ProtoFlush),
|
writer: &mut (impl BackendProtoWriter + ProtoFlush),
|
||||||
reader: &mut impl FrontendProtoReader,
|
reader: &mut impl FrontendProtoReader,
|
||||||
response: HandshakeResponse,
|
response: HandshakeResponse,
|
||||||
) -> Result<HandshakeRequest, ServerHandshakeError> {
|
) -> Result<HandshakeRequest, ServerHandshakeError> {
|
||||||
|
|
||||||
|
// Check if client requested SSL
|
||||||
match &reader.peek_special_message().await? {
|
match &reader.peek_special_message().await? {
|
||||||
Some(msg @ SpecialMessage::SSLRequest) => {
|
Some(msg @ SpecialMessage::SSLRequest) => {
|
||||||
reader.consume_special_message(msg).await?;
|
reader.consume_special_message(msg).await?;
|
||||||
|
|
@ -23,6 +27,7 @@ pub async fn do_server_handshake(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for mandatory StartupMessage
|
||||||
let startup_message = match &reader.peek_special_message().await? {
|
let startup_message = match &reader.peek_special_message().await? {
|
||||||
Some(msg @ SpecialMessage::StartupMessage(data)) => {
|
Some(msg @ SpecialMessage::StartupMessage(data)) => {
|
||||||
reader.consume_special_message(msg).await?;
|
reader.consume_special_message(msg).await?;
|
||||||
|
|
@ -33,15 +38,18 @@ pub async fn do_server_handshake(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Authenticate client
|
||||||
writer
|
writer
|
||||||
.write_proto(BackendMessage::from(AuthenticationOkData { status: 0 }))
|
.write_proto(BackendMessage::from(AuthenticationOkData { status: 0 }))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// Send server parameters
|
||||||
let messages: Vec<BackendMessage> = response.into();
|
let messages: Vec<BackendMessage> = response.into();
|
||||||
for message in messages {
|
for message in messages {
|
||||||
writer.write_proto(message).await?;
|
writer.write_proto(message).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finish the handshake
|
||||||
writer
|
writer
|
||||||
.write_proto(BackendMessage::from(ReadyForQueryData { status: b'I' }))
|
.write_proto(BackendMessage::from(ReadyForQueryData { status: b'I' }))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
//! # PostgreSQL 16 Protocol
|
||||||
|
//! Low-level PostgreSQL protocol implementation for the version 16, protocol version 3.0.
|
||||||
|
//! Includes server and client side handshake with no password authentication.
|
||||||
|
|
||||||
pub mod handshake;
|
pub mod handshake;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod reader;
|
pub mod reader;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue