Skip to content

Commit 9c521f0

Browse files
authored
parse startup client parameters (#16)
1 parent 4aa9c3d commit 9c521f0

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

src/client.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use tokio::io::{AsyncReadExt, BufReader};
88
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
99
use tokio::net::TcpStream;
1010

11+
use std::collections::HashMap;
12+
1113
use crate::config::Role;
1214
use crate::errors::Error;
1315
use crate::messages::*;
@@ -52,6 +54,9 @@ pub struct Client {
5254
// Unless client specifies, route queries to the servers that have this role,
5355
// e.g. primary or replicas or any.
5456
default_server_role: Option<Role>,
57+
58+
// Client parameters, e.g. user, client_encoding, etc.
59+
parameters: HashMap<String, String>,
5560
}
5661

5762
impl Client {
@@ -96,7 +101,7 @@ impl Client {
96101
// Regular startup message.
97102
196608 => {
98103
// TODO: perform actual auth.
99-
// TODO: record startup parameters client sends over.
104+
let parameters = parse_startup(bytes.clone())?;
100105

101106
// Generate random backend ID and secret key
102107
let process_id: i32 = rand::random();
@@ -121,6 +126,7 @@ impl Client {
121126
secret_key: secret_key,
122127
client_server_map: client_server_map,
123128
default_server_role: default_server_role,
129+
parameters: parameters,
124130
});
125131
}
126132

@@ -141,6 +147,7 @@ impl Client {
141147
secret_key: secret_key,
142148
client_server_map: client_server_map,
143149
default_server_role: default_server_role,
150+
parameters: HashMap::new(),
144151
});
145152
}
146153

src/messages.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use bytes::{BufMut, BytesMut};
1+
use bytes::{Buf, BufMut, BytesMut};
22
use md5::{Digest, Md5};
33
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
44
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
55
use tokio::net::TcpStream;
66

7+
use std::collections::HashMap;
8+
79
use crate::errors::Error;
810

911
// This is a funny one. `psql` parses this to figure out which
@@ -105,6 +107,51 @@ pub async fn startup(stream: &mut TcpStream, user: &str, database: &str) -> Resu
105107
}
106108
}
107109

110+
/// Parse StartupMessage parameters.
111+
/// e.g. user, database, application_name, etc.
112+
pub fn parse_startup(mut bytes: BytesMut) -> Result<HashMap<String, String>, Error> {
113+
let mut result = HashMap::new();
114+
let mut buf = Vec::new();
115+
let mut tmp = String::new();
116+
117+
while bytes.has_remaining() {
118+
let mut c = bytes.get_u8();
119+
120+
// Null-terminated C-strings.
121+
while c != 0 {
122+
tmp.push(c as char);
123+
c = bytes.get_u8();
124+
}
125+
126+
if tmp.len() > 0 {
127+
buf.push(tmp.clone());
128+
tmp.clear();
129+
}
130+
}
131+
132+
// Expect pairs of name and value
133+
// and at least one pair to be present.
134+
if buf.len() % 2 != 0 && buf.len() >= 2 {
135+
return Err(Error::ClientBadStartup);
136+
}
137+
138+
let mut i = 0;
139+
while i < buf.len() {
140+
let name = buf[i].clone();
141+
let value = buf[i + 1].clone();
142+
let _ = result.insert(name, value);
143+
i += 2;
144+
}
145+
146+
// Minimum required parameters
147+
// I want to have the user at the very minimum, according to the protocol spec.
148+
if !result.contains_key("user") {
149+
return Err(Error::ClientBadStartup);
150+
}
151+
152+
Ok(result)
153+
}
154+
108155
/// Send password challenge response to the server.
109156
/// This is the MD5 challenge.
110157
pub async fn md5_password(

0 commit comments

Comments
 (0)