diff --git a/Cargo.lock b/Cargo.lock index ddb3ac8..5c4af8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "anyhow" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" + [[package]] name = "async-trait" version = "0.1.74" @@ -28,6 +34,12 @@ dependencies = [ "syn", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "backtrace" version = "0.3.69" @@ -62,6 +74,12 @@ dependencies = [ "virtue", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bytes" version = "1.5.0" @@ -83,18 +101,43 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "client" +version = "0.1.0" +dependencies = [ + "anyhow", + "proto", + "tokio", +] + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + [[package]] name = "libc" version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "memchr" version = "2.6.4" @@ -114,6 +157,27 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.32.1" @@ -123,6 +187,29 @@ dependencies = [ "memchr", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -157,12 +244,27 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.193" @@ -183,6 +285,15 @@ dependencies = [ "syn", ] +[[package]] +name = "server" +version = "0.1.0" +dependencies = [ + "anyhow", + "proto", + "tokio", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -241,14 +352,21 @@ dependencies = [ [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", + "socket2", "tokio-macros", + "windows-sys", ] [[package]] @@ -273,3 +391,75 @@ name = "virtue" version = "0.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index 69e4ac6..714e4bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" members = [ "minisql", - "proto" "proto", + "server", "client" ] diff --git a/server/Cargo.toml b/server/Cargo.toml new file mode 100644 index 0000000..bca61ec --- /dev/null +++ b/server/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "server" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = { version = "1.35.1", features = ["full"] } +anyhow = "1.0.76" +proto = { path = "../proto" } \ No newline at end of file diff --git a/server/src/main.rs b/server/src/main.rs new file mode 100644 index 0000000..f5ea267 --- /dev/null +++ b/server/src/main.rs @@ -0,0 +1,184 @@ +use proto::handshake::response::HandshakeResponse; +use proto::handshake::server::do_server_handshake; +use proto::message::backend::{ + BackendMessage, ColumnDescription, CommandCompleteData, DataRowData, ErrorResponseData, + ReadyForQueryData, RowDescriptionData, +}; +use proto::message::frontend::FrontendMessage; +use proto::reader::oneway::OneWayProtoReader; +use proto::reader::protoreader::ProtoReader; +use proto::writer::backend::BackendProtoWriter; +use proto::writer::protowriter::{ProtoFlush, ProtoWriter}; +use tokio::io::{BufReader, BufWriter}; +use tokio::net::{TcpListener, TcpStream}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let addr = "0.0.0.0:5432"; + let listener = TcpListener::bind(&addr).await?; + println!("Server started at {addr}"); + + loop { + let (socket, _) = listener.accept().await?; + println!("New client connected: {}", socket.peer_addr()?); + tokio::spawn(async move { + let reason = handle_stream(socket).await; + println!("Client disconnected: {reason:?}"); + }); + } +} + +async fn handle_stream(mut stream: TcpStream) -> anyhow::Result<()> { + let (reader, writer) = stream.split(); + let mut writer = ProtoWriter::new(BufWriter::new(writer)); + let mut reader = ProtoReader::new(BufReader::new(reader), 1024); + + let response = HandshakeResponse::new("minisql", 123, 123); + let request = do_server_handshake(&mut writer, &mut reader, response).await?; + + println!("Handshake complete:\n{request:?}"); + + loop { + println!("Waiting for next message"); + let next: FrontendMessage = reader.read_proto().await?; + + match next { + FrontendMessage::Terminate => { + println!("Received Terminate"); + break; + } + FrontendMessage::Query(data) => { + println!("Received Query: {:?}", data); + if data.query.as_str().contains("car") { + println!("Sending error message"); + send_error_response(&mut writer, "Car not found").await?; + } else if data.query.as_str().to_lowercase().contains("select") { + println!("Sending table"); + send_query_repsonse(&mut writer).await?; + } else { + println!("Sending empty query"); + send_empty_query(&mut writer).await?; + } + send_ready_for_query(&mut writer).await?; + } + } + writer.flush().await?; + } + + Ok(()) +} + +async fn send_error_response( + writer: &mut impl BackendProtoWriter, + error_message: &str, +) -> anyhow::Result<()> { + writer + .write_proto( + ErrorResponseData { + code: b'M', + message: error_message.to_string().into(), + } + .into(), + ) + .await?; + + Ok(()) +} + +async fn send_ready_for_query(writer: &mut impl BackendProtoWriter) -> anyhow::Result<()> { + writer + .write_proto(BackendMessage::from(ReadyForQueryData { status: b'I' })) + .await?; + + Ok(()) +} + +async fn send_empty_query(writer: &mut impl BackendProtoWriter) -> anyhow::Result<()> { + writer + .write_proto(BackendMessage::EmptyQueryResponse) + .await?; + + Ok(()) +} + +async fn send_row_description(writer: &mut impl BackendProtoWriter) -> anyhow::Result<()> { + let columns = vec![ + ColumnDescription { + name: "id".to_string().into(), + table_oid: 123, + column_index: 1, + type_oid: 23, + type_size: 4, + type_modifier: -1, + format_code: 0, + }, + ColumnDescription { + name: "argument".to_string().into(), + table_oid: 123, + column_index: 2, + type_oid: 23, + type_size: 4, + type_modifier: -1, + format_code: 0, + }, + ColumnDescription { + name: "description".to_string().into(), + table_oid: 123, + column_index: 3, + type_oid: 1043, + type_size: 32, + type_modifier: -1, + format_code: 0, + }, + ]; + + writer + .write_proto( + RowDescriptionData { + columns: columns.into(), + } + .into(), + ) + .await?; + + Ok(()) +} + +async fn send_query_repsonse(writer: &mut impl BackendProtoWriter) -> anyhow::Result<()> { + send_row_description(writer).await?; + + write_row(writer, b"0", b"1337", b"auto").await?; + write_row(writer, b"1", b"69", b"bus").await?; + write_row(writer, b"2", b"420", b"kolo").await?; + + writer + .write_proto( + CommandCompleteData { + tag: "SELECT 3".to_string().into(), + } + .into(), + ) + .await?; + + Ok(()) +} + +async fn write_row( + writer: &mut impl BackendProtoWriter, + first: &[u8], + second: &[u8], + third: &[u8], +) -> anyhow::Result<()> { + let row_data = vec![ + first.to_vec().into(), + second.to_vec().into(), + third.to_vec().into(), + ] + .into(); + + writer + .write_proto(DataRowData { columns: row_data }.into()) + .await?; + + Ok(()) +}