postgres_protocol/message/
startup.rs1#![allow(missing_docs)]
2
3use std::collections::HashMap;
4use std::io;
5use std::iter::FromIterator;
6
7use crate::message::shared::Buffer;
8use byteorder::{BigEndian, ReadBytesExt};
9use bytes::{BufMut, Bytes, BytesMut};
10
11#[derive(Debug)]
12pub enum StartupRequest {
13 Startup(Bytes),
14 Cancel(CancelData),
15 SSLRequest,
16 GSSEncRequest,
17 Password(Bytes),
18}
19
20#[derive(Debug)]
21pub struct CancelData {
22 pub process_id: i32,
23 pub secret_key: i32,
24}
25
26impl StartupRequest {
27 #[inline]
28 pub fn parse_without_tag(buf: &mut BytesMut) -> io::Result<Option<StartupRequest>> {
29 if buf.len() < 4 {
30 let to_read = 4 - buf.len();
31 buf.reserve(to_read);
32 return Ok(None);
33 }
34
35 let len = (&buf[0..4]).read_u32::<BigEndian>().unwrap() as usize;
36
37 const MIN_STARTUP_LEN: usize = 4;
39 const MAX_STARTUP_LEN: usize = 10000;
40
41 if !(MIN_STARTUP_LEN..=MAX_STARTUP_LEN).contains(&len) {
42 return Err(io::Error::new(
43 io::ErrorKind::InvalidInput,
44 "invalid message length: parsing u32",
45 ));
46 }
47
48 if buf.len() < len {
50 let to_read = len - buf.len();
51 buf.reserve(to_read);
52 return Ok(None);
53 }
54
55 let mut buf = Buffer {
56 bytes: buf.split_to(len).freeze(),
57 idx: 4,
58 };
59
60 let code = buf.read_u32::<BigEndian>()?;
61 let message = match code {
62 196_608 => {
64 let data = buf.read_all();
65 StartupRequest::Startup(data)
66 }
67
68 80_877_102 => {
70 let process_id = buf.read_i32::<BigEndian>()?;
71 let secret_key = buf.read_i32::<BigEndian>()?;
72 StartupRequest::Cancel(CancelData {
73 process_id,
74 secret_key,
75 })
76 }
77
78 80_877_103 => StartupRequest::SSLRequest,
80
81 80_877_104 => StartupRequest::GSSEncRequest,
83
84 _ => {
85 return Err(io::Error::new(
86 io::ErrorKind::InvalidInput,
87 format!("unknown startup message code `{}`", code),
88 ));
89 }
90 };
91
92 if !buf.is_empty() {
93 return Err(io::Error::new(
94 io::ErrorKind::InvalidInput,
95 "invalid message length: expected buffer to be empty",
96 ));
97 }
98
99 Ok(Some(message))
100 }
101
102 #[inline]
103 pub fn parse_with_tag(buf: &mut BytesMut) -> io::Result<Option<StartupRequest>> {
104 if buf.len() < 5 {
105 let to_read = 5 - buf.len();
106 buf.reserve(to_read);
107 return Ok(None);
108 }
109
110 let len = (&buf[1..5]).read_u32::<BigEndian>().unwrap() as usize;
111
112 const MIN_STARTUP_LEN: usize = 4;
114 const MAX_STARTUP_LEN: usize = 10000;
115
116 if !(MIN_STARTUP_LEN..=MAX_STARTUP_LEN).contains(&len) {
117 return Err(io::Error::new(
118 io::ErrorKind::InvalidInput,
119 "invalid message length: parsing u32",
120 ));
121 }
122
123 if buf.len() < len {
125 let to_read = len - buf.len();
126 buf.reserve(to_read);
127 return Ok(None);
128 }
129
130 let tag = buf[0] as char;
131 let mut buf = Buffer {
132 bytes: buf.split_to(len).freeze(),
133 idx: 5,
134 };
135
136 let message = match tag {
137 'p' => {
139 let passwd = buf.read_cstr()?;
140 StartupRequest::Password(passwd)
141 }
142
143 _ => {
144 return Err(io::Error::new(
145 io::ErrorKind::InvalidInput,
146 format!("unknown startup message tag '{}'", tag),
147 ));
148 }
149 };
150
151 if !buf.is_empty() {
152 return Err(io::Error::new(
153 io::ErrorKind::InvalidInput,
154 "invalid message length: expected buffer to be empty",
155 ));
156 }
157
158 Ok(Some(message))
159 }
160}
161
162#[derive(Debug)]
163pub struct StartupData {
164 pub parameters: HashMap<String, Bytes>,
165}
166
167impl StartupData {
168 pub fn parse(bytes: Bytes) -> io::Result<StartupData> {
169 let mut buf = Buffer { bytes, idx: 0 };
170
171 let pairs = std::iter::from_fn(move || {
172 let key = buf.read_cstr().ok()?;
173 if key.is_empty() {
174 return None;
175 }
176 let value = buf.read_cstr().ok()?;
177 let key_str = String::from_utf8(key.to_vec()).ok()?;
178 Some((key_str, value))
179 });
180
181 let parameters = HashMap::from_iter(pairs);
182 Ok(StartupData { parameters })
183 }
184}
185
186#[derive(Debug)]
187pub enum StartupResponse {
188 AuthenticationOk,
189 AuthenticationMD5Password { salt: [u8; 4] },
190 SSLResponse(bool),
191 GSSEncResponse(bool),
192 ErrorResponse { code: &'static str, message: String },
193 ParameterStatus { key: String, value: Bytes },
194 BackendKeyData { process_id: i32, secret_key: i32 },
195 ReadyForQuery,
196}
197
198impl StartupResponse {
199 pub fn encode(&self, dst: &mut BytesMut) -> Result<(), io::Error> {
200 match self {
201 StartupResponse::AuthenticationOk => {
202 dst.reserve(1 + 8);
203 dst.put_u8(b'R');
204 dst.put_u32(8);
205 dst.put_u32(0); }
207 StartupResponse::AuthenticationMD5Password { salt } => {
208 dst.reserve(1 + 12);
209 dst.put_u8(b'R');
210 dst.put_u32(12);
211 dst.put_slice(&salt[..]);
212 dst.put_u32(0); }
214 StartupResponse::SSLResponse(ok) => {
215 dst.reserve(1);
216 dst.put_u8(if *ok { b'S' } else { b'N' });
217 }
218 StartupResponse::GSSEncResponse(ok) => {
219 dst.reserve(1);
220 dst.put_u8(if *ok { b'G' } else { b'N' });
221 }
222 StartupResponse::ErrorResponse { code, message } => {
223 let code_bytes = code.as_bytes();
224 let err_bytes = message.as_bytes();
225 let len =
226 4 + (1 + 5 + 1) + (1 + code_bytes.len() + 1) + (1 + err_bytes.len() + 1) + 1;
227 dst.reserve(1 + len);
228 dst.put_u8(b'E');
229 dst.put_u32(len as u32);
230
231 dst.put_u8(b'S');
232 dst.put_slice(b"FATAL");
233 dst.put_u8(0);
234
235 dst.put_u8(b'C');
237 dst.put_slice(code_bytes);
238 dst.put_u8(0);
239
240 dst.put_u8(b'M');
241 dst.put_slice(err_bytes);
242 dst.put_u8(0);
243 dst.put_u8(0);
244 }
245
246 StartupResponse::ParameterStatus { key, value } => {
247 let key_bytes = key.as_bytes();
248 let value_bytes = value.as_ref();
249
250 let len = 4 + (key_bytes.len() + 1) + (value_bytes.len() + 1);
251 dst.reserve(1 + len);
252 dst.put_u8(b'S');
253 dst.put_u32(len as u32);
254
255 dst.put_slice(key_bytes);
256 dst.put_u8(0);
257 dst.put_slice(value_bytes);
258 dst.put_u8(0);
259 }
260
261 StartupResponse::BackendKeyData {
262 process_id,
263 secret_key,
264 } => {
265 dst.reserve(1 + 12);
266 dst.put_u8(b'K');
267 dst.put_u32(12);
268 dst.put_i32(*process_id);
269 dst.put_i32(*secret_key);
270 }
271
272 StartupResponse::ReadyForQuery => {
273 dst.reserve(1 + 5);
274 dst.put_u8(b'Z');
275 dst.put_u32(5);
276 dst.put_u8(b'I');
277 }
278 }
279 Ok(())
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use fallible_iterator::FallibleIterator;
286
287 use super::*;
288 use crate::message::backend::Message;
289
290 #[test]
291 fn error_response_is_parseable_by_clients() {
292 let mut buf = BytesMut::new();
293 StartupResponse::ErrorResponse {
294 code: "3D000",
295 message: "connection rejected: unknown database".to_string(),
296 }
297 .encode(&mut buf)
298 .unwrap();
299
300 let body = match Message::parse(&mut buf).unwrap() {
301 Some(Message::ErrorResponse(body)) => body,
302 _ => panic!("expected an ErrorResponse"),
303 };
304 assert!(buf.is_empty());
305
306 let fields: Vec<(u8, Vec<u8>)> = body
307 .fields()
308 .map(|f| Ok((f.type_(), f.value_bytes().to_vec())))
309 .collect()
310 .unwrap();
311 assert_eq!(
312 fields,
313 vec![
314 (b'S', b"FATAL".to_vec()),
315 (b'C', b"3D000".to_vec()),
316 (b'M', b"connection rejected: unknown database".to_vec()),
317 ]
318 );
319 }
320}