├── rustfmt.toml ├── .gitignore ├── .travis.yml ├── .github └── workflows │ └── build-and-test.yml ├── examples ├── sasl.rs ├── unixsocket.rs └── basic.rs ├── Cargo.toml ├── src ├── lib.rs ├── version.rs ├── proto │ ├── mod.rs │ ├── binarydef.rs │ └── binary.rs └── client │ └── mod.rs ├── LICENSE └── README.md /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 120 2 | fn_call_width = 120 3 | reorder_imports = true 4 | use_try_shorthand = true 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | Cargo.lock 13 | 14 | # System 15 | .DS_Store 16 | Thumbs.db 17 | 18 | # Editor 19 | *~ 20 | .swp 21 | *.sublime-* 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | jobs: 3 | include: 4 | - rust: stable 5 | env: FEATURES="" 6 | - rust: nightly 7 | env: FEATURES="--features nightly" 8 | 9 | services: 10 | - memcached 11 | 12 | script: 13 | - cargo check $FEATURES --all --all-targets 14 | - cargo build $FEATURES 15 | - cargo test $FEATURES --no-fail-fast 16 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: niden/actions-memcached@v7 20 | - name: Build 21 | run: cargo build --verbose 22 | - name: Run tests 23 | run: cargo test --verbose 24 | -------------------------------------------------------------------------------- /examples/sasl.rs: -------------------------------------------------------------------------------- 1 | extern crate memcached; 2 | 3 | use memcached::proto::{Operation, ProtoType}; 4 | use memcached::Client; 5 | 6 | fn main() { 7 | let servers = [("tcp://my-sasl-memcached-server.com:11211", 1)]; 8 | let mut client = Client::connect_sasl(&servers, ProtoType::Binary, "my-username", "my-password").unwrap(); 9 | 10 | client.set(b"Foo", b"Bar", 0xdead_beef, 2).unwrap(); 11 | let (value, flags) = client.get(b"Foo").unwrap(); 12 | assert_eq!(&value[..], b"Bar"); 13 | assert_eq!(flags, 0xdead_beef); 14 | } 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memcached-rs" 3 | version = "0.4.2" 4 | authors = ["Y. T. CHUNG "] 5 | description = "A MemCached Library in Rust" 6 | repository = "https://github.com/zonyitoo/memcached-rs" 7 | keywords = ["memcached"] 8 | license = "MIT/Apache-2.0" 9 | edition = "2018" 10 | 11 | [lib] 12 | name = "memcached" 13 | 14 | [features] 15 | nightly = [] 16 | 17 | [dependencies] 18 | byteorder = "1.2" 19 | semver = "1.0" 20 | fastrand = "1.3" 21 | conhash = "0.5" 22 | log = "0.4" 23 | bufstream = "0.1" 24 | bytes = "1.2" 25 | 26 | [target.'cfg(unix)'.dependencies] 27 | unix_socket = "0.5" 28 | 29 | [dev-dependencies] 30 | env_logger = "0.9" 31 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Y. T. Chung 2 | // Licensed under the Apache License, Version 2.0 3 | // or the MIT 5 | // license , 6 | // at your option. All files in the project carrying such 7 | // notice may not be copied, modified, or distributed except 8 | // according to those terms. 9 | 10 | #![crate_type = "lib"] 11 | #![crate_name = "memcached"] 12 | #![allow(clippy::type_complexity)] // For `crate::proto::MemCachedResult, (Vec, u32)>>` 13 | #![cfg_attr(feature = "nightly", feature(test))] 14 | #[cfg(feature = "nightly")] 15 | extern crate test; 16 | 17 | pub use client::Client; 18 | 19 | pub mod client; 20 | pub mod proto; 21 | -------------------------------------------------------------------------------- /examples/unixsocket.rs: -------------------------------------------------------------------------------- 1 | extern crate memcached; 2 | #[macro_use] 3 | extern crate log; 4 | extern crate env_logger; 5 | 6 | use memcached::proto::{CasOperation, NoReplyOperation, Operation, ProtoType}; 7 | use memcached::Client; 8 | 9 | fn main() { 10 | env_logger::init(); 11 | 12 | let servers = [("unix:///tmp/memcached.sock", 10)]; 13 | info!("Using servers: {:?} with Binary protocol", servers); 14 | let mut client = Client::connect(&servers, ProtoType::Binary).unwrap(); 15 | 16 | client.set(b"Foo", b"Bar", 0xdead_beef, 2).unwrap(); 17 | let (value, flags) = client.get(b"Foo").unwrap(); 18 | assert_eq!(&value[..], b"Bar"); 19 | assert_eq!(flags, 0xdead_beef); 20 | 21 | client.set_noreply(b"key:dontreply", b"1", 0x00_00_00_01, 20).unwrap(); 22 | 23 | let (_, cas_val) = client.increment_cas(b"key:numerical", 10, 1, 20, 0).unwrap(); 24 | client.increment_cas(b"key:numerical", 1, 1, 20, cas_val).unwrap(); 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Y. T. CHUNG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | extern crate memcached; 2 | #[macro_use] 3 | extern crate log; 4 | extern crate env_logger; 5 | 6 | use std::thread; 7 | 8 | use memcached::proto::{CasOperation, NoReplyOperation, Operation, ProtoType}; 9 | use memcached::Client; 10 | 11 | fn main() { 12 | env_logger::init(); 13 | 14 | let servers = [("tcp://127.0.0.1:11211", 1)]; 15 | info!("Using servers: {:?} with Binary protocol", servers); 16 | let mut client = Client::connect(&servers, ProtoType::Binary).unwrap(); 17 | 18 | client.set(b"Foo", b"Bar", 0xdead_beef, 2).unwrap(); 19 | let (value, flags) = client.get(b"Foo").unwrap(); 20 | assert_eq!(&value[..], b"Bar"); 21 | assert_eq!(flags, 0xdead_beef); 22 | 23 | client.set_noreply(b"key:dontreply", b"1", 0x00_00_00_01, 20).unwrap(); 24 | 25 | let (_, cas_val) = client.increment_cas(b"key:numerical", 10, 1, 20, 0).unwrap(); 26 | client.increment_cas(b"key:numerical", 1, 1, 20, cas_val).unwrap(); 27 | 28 | let mut handlers = Vec::new(); 29 | for _ in 0..4 { 30 | let handler = thread::spawn(move || { 31 | let mut client = Client::connect(&servers, ProtoType::Binary).unwrap(); 32 | let (_, _, mut cas) = client.get_cas(b"key:dontreply").unwrap(); 33 | for _ in 0..100 { 34 | debug!("Setting in {:?}", thread::current()); 35 | client.set_cas(b"key:dontreply", b"1", 0x00_10_01, 20, cas).unwrap(); 36 | cas = client.get_cas(b"key:dontreply").unwrap().2; 37 | } 38 | }); 39 | handlers.push(handler); 40 | } 41 | 42 | for hdl in handlers { 43 | hdl.join().unwrap(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/version.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Y. T. Chung 2 | // Licensed under the Apache License, Version 2.0 3 | // or the MIT 5 | // license , 6 | // at your option. All files in the project carrying such 7 | // notice may not be copied, modified, or distributed except 8 | // according to those terms. 9 | 10 | //! Memcached version 11 | 12 | use std::fmt::{self, Display, Formatter}; 13 | use std::str::FromStr; 14 | 15 | /// Memcached version 16 | /// 17 | /// Version(major, minor, patch) 18 | #[derive(Copy, Debug)] 19 | pub struct Version(u32, u32, u32); 20 | 21 | impl Version { 22 | pub fn new(major: u32, minor: u32, patch: u32) -> Version { 23 | Version(major, minor, patch) 24 | } 25 | } 26 | 27 | impl Display for Version { 28 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 29 | let &Version(major, minor, patch) = self; 30 | write!(f, "{}:{}:{}", major, minor, patch) 31 | } 32 | } 33 | 34 | macro_rules! try_option( 35 | ($inp:expr) => ( 36 | match $inp { 37 | Some(v) => { v }, 38 | None => { return None; }, 39 | } 40 | ); 41 | ); 42 | 43 | impl FromStr for Version { 44 | fn from_str(s: &str) -> Option { 45 | let mut sp = s.split('.'); 46 | let major = match sp.next() { 47 | Some(s) => try_option!(s.parse()), 48 | None => return None, 49 | }; 50 | let minor = match sp.next() { 51 | Some(s) => try_option!(s.parse()), 52 | None => 0, 53 | }; 54 | let patch = match sp.next() { 55 | Some(s) => try_option!(s.parse()), 56 | None => 0, 57 | }; 58 | 59 | Some(Version::new(major, minor, patch)) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # memcached-rs 2 | 3 | [![Build & Test](https://github.com/zonyitoo/memcached-rs/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/zonyitoo/memcached-rs/actions/workflows/build-and-test.yml) 4 | [![crates.io](https://img.shields.io/crates/v/memcached-rs.svg)](https://crates.io/crates/memcached-rs) 5 | [![dependency status](https://deps.rs/repo/github/zonyitoo/memcached-rs/status.svg)](https://deps.rs/repo/github/zonyitoo/memcached-rs) 6 | 7 | Memcached library in Rust 8 | 9 | ## Usage 10 | 11 | ```rust 12 | use std::collections::TreeMap; 13 | 14 | use memcached::Client; 15 | use memcached::proto::{Operation, MultiOperation, NoReplyOperation, CasOperation, ProtoType}; 16 | 17 | fn main() { 18 | let servers = [ 19 | ("tcp://127.0.0.1:11211", 1), 20 | ]; 21 | let mut client = Client::connect(&servers, ProtoType::Binary).unwrap(); 22 | 23 | client.set(b"Foo", b"Bar", 0xdeadbeef, 2).unwrap(); 24 | let (value, flags) = client.get(b"Foo").unwrap(); 25 | assert_eq!(value.as_slice(), b"Bar"); 26 | assert_eq!(flags, 0xdeadbeef); 27 | 28 | client.set_noreply(b"key:dontreply", b"1", 0x00000001, 20).unwrap(); 29 | 30 | let (_, cas_val) = client.increment_cas(b"key:numerical", 10, 1, 20, 0).unwrap(); 31 | client.increment_cas(b"key:numerical", 1, 1, 20, cas_val).unwrap(); 32 | } 33 | ``` 34 | 35 | Run `cargo doc --open` for more details. 36 | 37 | ### SASL authentication 38 | 39 | TCP connections support `PLAIN` SASL authentication: 40 | 41 | ```rust 42 | use memcached::proto::{Operation, ProtoType}; 43 | use memcached::Client; 44 | 45 | fn main() { 46 | let servers = [ 47 | ("tcp://my-sasl-memcached-server.com:11211", 1) 48 | ]; 49 | let mut client = Client::connect_sasl(&servers, ProtoType::Binary, "my-username", "my-password").unwrap(); 50 | 51 | client.set(b"Foo", b"Bar", 0xdeadbeef, 2).unwrap(); 52 | let (value, flags) = client.get(b"Foo").unwrap(); 53 | assert_eq!(&value[..], b"Bar"); 54 | assert_eq!(flags, 0xdeadbeef); 55 | } 56 | ``` 57 | 58 | ## TODO 59 | 60 | * Auto-disable failed servers 61 | 62 | ## License 63 | 64 | Licensed under either of 65 | 66 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 67 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 68 | 69 | at your option. 70 | 71 | ### Contribution 72 | 73 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 74 | -------------------------------------------------------------------------------- /src/proto/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Y. T. Chung 2 | // Licensed under the Apache License, Version 2.0 3 | // or the MIT 5 | // license , 6 | // at your option. All files in the project carrying such 7 | // notice may not be copied, modified, or distributed except 8 | // according to those terms. 9 | 10 | //! Memcached protocol 11 | 12 | use std::collections::{BTreeMap, HashMap}; 13 | use std::convert::From; 14 | use std::error; 15 | use std::fmt::{self, Display}; 16 | use std::io; 17 | 18 | use semver::Version; 19 | 20 | pub use self::binary::BinaryProto; 21 | 22 | pub mod binary; 23 | mod binarydef; 24 | 25 | /// Protocol type 26 | #[derive(Copy, Clone)] 27 | pub enum ProtoType { 28 | Binary, 29 | } 30 | 31 | #[derive(Debug)] 32 | pub enum Error { 33 | BinaryProtoError(binary::Error), 34 | IoError(io::Error), 35 | OtherError { desc: &'static str, detail: Option }, 36 | } 37 | 38 | pub type MemCachedResult = Result; 39 | 40 | impl error::Error for Error {} 41 | 42 | impl Display for Error { 43 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 44 | match *self { 45 | Error::BinaryProtoError(ref err) => err.fmt(f), 46 | Error::IoError(ref err) => err.fmt(f), 47 | Error::OtherError { desc, ref detail } => { 48 | write!(f, "{}", desc)?; 49 | match *detail { 50 | Some(ref s) => write!(f, " ({})", s), 51 | None => Ok(()), 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | impl From for Error { 59 | fn from(err: io::Error) -> Error { 60 | Error::IoError(err) 61 | } 62 | } 63 | 64 | impl From for Error { 65 | fn from(err: binary::Error) -> Error { 66 | Error::BinaryProtoError(err) 67 | } 68 | } 69 | 70 | pub trait Proto: 71 | Operation + MultiOperation + ServerOperation + NoReplyOperation + CasOperation + AuthOperation 72 | { 73 | // fn clone(&self) -> Box; 74 | } 75 | 76 | impl Proto for T where 77 | T: Operation + MultiOperation + ServerOperation + NoReplyOperation + CasOperation + AuthOperation 78 | { 79 | } 80 | 81 | pub trait Operation { 82 | fn set(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()>; 83 | fn add(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()>; 84 | fn delete(&mut self, key: &[u8]) -> MemCachedResult<()>; 85 | fn replace(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()>; 86 | fn get(&mut self, key: &[u8]) -> MemCachedResult<(Vec, u32)>; 87 | fn getk(&mut self, key: &[u8]) -> MemCachedResult<(Vec, Vec, u32)>; 88 | fn increment(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult; 89 | fn decrement(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult; 90 | fn append(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()>; 91 | fn prepend(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()>; 92 | fn touch(&mut self, key: &[u8], expiration: u32) -> MemCachedResult<()>; 93 | } 94 | 95 | pub trait CasOperation { 96 | fn set_cas(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32, cas: u64) -> MemCachedResult; 97 | fn add_cas(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult; 98 | fn replace_cas(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32, cas: u64) -> MemCachedResult; 99 | fn get_cas(&mut self, key: &[u8]) -> MemCachedResult<(Vec, u32, u64)>; 100 | fn getk_cas(&mut self, key: &[u8]) -> MemCachedResult<(Vec, Vec, u32, u64)>; 101 | fn increment_cas( 102 | &mut self, 103 | key: &[u8], 104 | amount: u64, 105 | initial: u64, 106 | expiration: u32, 107 | cas: u64, 108 | ) -> MemCachedResult<(u64, u64)>; 109 | fn decrement_cas( 110 | &mut self, 111 | key: &[u8], 112 | amount: u64, 113 | initial: u64, 114 | expiration: u32, 115 | cas: u64, 116 | ) -> MemCachedResult<(u64, u64)>; 117 | fn append_cas(&mut self, key: &[u8], value: &[u8], cas: u64) -> MemCachedResult; 118 | fn prepend_cas(&mut self, key: &[u8], value: &[u8], cas: u64) -> MemCachedResult; 119 | fn touch_cas(&mut self, key: &[u8], expiration: u32, cas: u64) -> MemCachedResult; 120 | } 121 | 122 | pub trait ServerOperation { 123 | fn quit(&mut self) -> MemCachedResult<()>; 124 | fn flush(&mut self, expiration: u32) -> MemCachedResult<()>; 125 | fn noop(&mut self) -> MemCachedResult<()>; 126 | fn version(&mut self) -> MemCachedResult; 127 | fn stat(&mut self) -> MemCachedResult>; 128 | } 129 | 130 | pub trait MultiOperation { 131 | fn set_multi(&mut self, kv: BTreeMap<&[u8], (&[u8], u32, u32)>) -> MemCachedResult<()>; 132 | fn delete_multi(&mut self, keys: &[&[u8]]) -> MemCachedResult<()>; 133 | fn increment_multi<'a>( 134 | &mut self, 135 | kv: HashMap<&'a [u8], (u64, u64, u32)>, 136 | ) -> MemCachedResult>; 137 | fn get_multi(&mut self, keys: &[&[u8]]) -> MemCachedResult, (Vec, u32)>>; 138 | } 139 | 140 | pub trait NoReplyOperation { 141 | fn set_noreply(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()>; 142 | fn add_noreply(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()>; 143 | fn delete_noreply(&mut self, key: &[u8]) -> MemCachedResult<()>; 144 | fn replace_noreply(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()>; 145 | fn increment_noreply(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult<()>; 146 | fn decrement_noreply(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult<()>; 147 | fn append_noreply(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()>; 148 | fn prepend_noreply(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()>; 149 | } 150 | 151 | #[derive(Debug)] 152 | pub enum AuthResponse { 153 | Continue(Vec), 154 | Succeeded, 155 | Failed, 156 | } 157 | 158 | pub trait AuthOperation { 159 | fn list_mechanisms(&mut self) -> MemCachedResult>; 160 | fn auth_start(&mut self, mech: &str, init: &[u8]) -> MemCachedResult; 161 | fn auth_continue(&mut self, mech: &str, data: &[u8]) -> MemCachedResult; 162 | } 163 | -------------------------------------------------------------------------------- /src/client/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Y. T. Chung 2 | // Licensed under the Apache License, Version 2.0 3 | // or the MIT 5 | // license , 6 | // at your option. All files in the project carrying such 7 | // notice may not be copied, modified, or distributed except 8 | // according to those terms. 9 | 10 | //! Memcached client 11 | 12 | use std::cell::RefCell; 13 | use std::collections::{BTreeMap, HashMap}; 14 | use std::io; 15 | use std::net::{SocketAddr, TcpStream, ToSocketAddrs}; 16 | use std::ops::Deref; 17 | use std::path::Path; 18 | use std::rc::Rc; 19 | use std::time::Duration; 20 | 21 | use conhash::{ConsistentHash, Node}; 22 | 23 | use bufstream::BufStream; 24 | 25 | #[cfg(unix)] 26 | use unix_socket::UnixStream; 27 | 28 | use crate::proto::{self, AuthResponse, MemCachedResult}; 29 | use crate::proto::{CasOperation, MultiOperation, NoReplyOperation, Operation, Proto}; 30 | 31 | struct Sasl<'a> { 32 | username: &'a str, 33 | password: &'a str, 34 | } 35 | 36 | struct ConnectOpts { 37 | connect_timeout: Option, 38 | read_timeout: Option, 39 | write_timeout: Option, 40 | } 41 | 42 | struct Server { 43 | pub proto: Box, 44 | addr: String, 45 | } 46 | 47 | impl Server { 48 | fn connect( 49 | addr: String, 50 | protocol: proto::ProtoType, 51 | o_sasl: &Option, 52 | connect_opts: &Option, 53 | ) -> io::Result { 54 | let proto = { 55 | let mut split = addr.split("://"); 56 | match protocol { 57 | proto::ProtoType::Binary => match (split.next(), split.next()) { 58 | (Some("tcp"), Some(addr)) => { 59 | let stream = match connect_opts.as_ref().and_then(|opts| opts.connect_timeout) { 60 | Some(timeout) => { 61 | let socket_addr: SocketAddr = addr.to_socket_addrs()?.next().unwrap(); 62 | TcpStream::connect_timeout(&socket_addr, timeout)? 63 | } 64 | None => TcpStream::connect(addr)?, 65 | }; 66 | if let Some(opts) = &connect_opts { 67 | stream.set_read_timeout(opts.read_timeout)?; 68 | stream.set_write_timeout(opts.write_timeout)?; 69 | } 70 | stream.set_nodelay(true)?; 71 | let mut proto = 72 | Box::new(proto::BinaryProto::new(BufStream::new(stream))) as Box; 73 | if let Some(sasl) = o_sasl { 74 | let auth_str = format!("\x00{}\x00{}", sasl.username, sasl.password); 75 | match proto.auth_start("PLAIN", auth_str.as_bytes()) { 76 | Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)), 77 | Ok(AuthResponse::Succeeded) => (), 78 | Ok(resp) => { 79 | let msg = format!("SASL auth failed with AuthResponse: {:?}", resp); 80 | return Err(io::Error::new(io::ErrorKind::Other, msg)); 81 | } 82 | } 83 | } 84 | proto 85 | } 86 | #[cfg(unix)] 87 | (Some("unix"), Some(addr)) => { 88 | let stream = UnixStream::connect(&Path::new(addr))?; 89 | if let Some(opts) = &connect_opts { 90 | stream.set_read_timeout(opts.read_timeout)?; 91 | stream.set_write_timeout(opts.write_timeout)?; 92 | } 93 | Box::new(proto::BinaryProto::new(BufStream::new(stream))) as Box 94 | } 95 | (Some(prot), _) => { 96 | panic!("Unsupported protocol: {}", prot); 97 | } 98 | _ => panic!("Malformed address"), 99 | }, 100 | } 101 | }; 102 | Ok(Server { proto, addr }) 103 | } 104 | } 105 | 106 | #[derive(Clone)] 107 | struct ServerRef(Rc>); 108 | 109 | impl Node for ServerRef { 110 | fn name(&self) -> String { 111 | self.0.borrow().addr.clone() 112 | } 113 | } 114 | 115 | impl Deref for ServerRef { 116 | type Target = Rc>; 117 | 118 | fn deref(&self) -> &Rc> { 119 | &self.0 120 | } 121 | } 122 | 123 | // impl Clone for Server { 124 | // fn clone(&self) -> Server { 125 | // Server { proto: self.proto.clone() } 126 | // } 127 | // } 128 | 129 | /// Memcached client 130 | /// 131 | /// ```ignore 132 | /// use memcached::client::{Client}; 133 | /// use memcached::proto::{CasOperation, MultiOperation, NoReplyOperation, Operation, ProtoType}; 134 | /// 135 | /// let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 136 | /// 137 | /// client.set(b"Foo", b"Bar", 0xdeadbeef, 2).unwrap(); 138 | /// let (value, flags) = client.get(b"Foo").unwrap(); 139 | /// assert_eq!(&value[..], b"Bar"); 140 | /// assert_eq!(flags, 0xdeadbeef); 141 | /// 142 | /// client.set_noreply(b"key:dontreply", b"1", 0x00000001, 20).unwrap(); 143 | /// 144 | /// let (_, cas_val) = client.increment_cas(b"key:numerical", 10, 1, 20, 0).unwrap(); 145 | /// client.increment_cas(b"key:numerical", 1, 1, 20, cas_val).unwrap(); 146 | /// ``` 147 | pub struct Client { 148 | servers: ConsistentHash, 149 | } 150 | 151 | impl Client { 152 | /// Connect to Memcached servers 153 | /// 154 | /// This function accept multiple servers, servers information should be represented 155 | /// as a array of tuples in this form 156 | /// 157 | /// `(address, weight)`. 158 | pub fn connect(svrs: &[(S, usize)], p: proto::ProtoType) -> io::Result { 159 | Client::conn(svrs, p, None, None) 160 | } 161 | 162 | /// Connect to Memcached servers with connect and/or IO timeouts 163 | /// 164 | /// This function accept multiple servers, servers information should be represented 165 | /// as a array of tuples in this form 166 | /// 167 | /// `(address, weight)`. 168 | pub fn connect_with_opts( 169 | svrs: &[(S, usize)], 170 | p: proto::ProtoType, 171 | connect_timeout: Option, 172 | read_timeout: Option, 173 | write_timeout: Option, 174 | ) -> io::Result { 175 | Client::conn( 176 | svrs, 177 | p, 178 | None, 179 | Some(ConnectOpts { 180 | connect_timeout, 181 | read_timeout, 182 | write_timeout, 183 | }), 184 | ) 185 | } 186 | 187 | /// Connect to Memcached servers that require SASL authentication 188 | /// 189 | /// This function accept multiple servers, servers information should be represented 190 | /// as a array of tuples in this form 191 | /// 192 | /// `(address, weight)`. 193 | pub fn connect_sasl( 194 | svrs: &[(S, usize)], 195 | p: proto::ProtoType, 196 | username: &str, 197 | password: &str, 198 | ) -> io::Result { 199 | Client::conn(svrs, p, Some(Sasl { username, password }), None) 200 | } 201 | 202 | /// Connect to Memcached servers that require SASL authentication with connect and/or I/O timeouts 203 | /// 204 | /// This function accept multiple servers, servers information should be represented 205 | /// as a array of tuples in this form 206 | /// 207 | /// `(address, weight)`. 208 | pub fn connect_sasl_with_opts( 209 | svrs: &[(S, usize)], 210 | p: proto::ProtoType, 211 | username: &str, 212 | password: &str, 213 | connect_timeout: Option, 214 | read_timeout: Option, 215 | write_timeout: Option, 216 | ) -> io::Result { 217 | Client::conn( 218 | svrs, 219 | p, 220 | Some(Sasl { username, password }), 221 | Some(ConnectOpts { 222 | connect_timeout, 223 | read_timeout, 224 | write_timeout, 225 | }), 226 | ) 227 | } 228 | 229 | fn conn( 230 | svrs: &[(S, usize)], 231 | p: proto::ProtoType, 232 | sasl: Option, 233 | opts: Option, 234 | ) -> io::Result { 235 | assert!(!svrs.is_empty(), "Server list should not be empty"); 236 | 237 | let mut servers = ConsistentHash::new(); 238 | for (addr, weight) in svrs.iter() { 239 | let svr = Server::connect(addr.to_string(), p, &sasl, &opts)?; 240 | servers.add(&ServerRef(Rc::new(RefCell::new(svr))), *weight); 241 | } 242 | 243 | Ok(Client { servers }) 244 | } 245 | 246 | fn find_server_by_key(&mut self, key: &[u8]) -> &mut ServerRef { 247 | self.servers.get_mut(key).expect("No valid server found") 248 | } 249 | } 250 | 251 | impl Operation for Client { 252 | fn set(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 253 | let server = self.find_server_by_key(key); 254 | server.borrow_mut().proto.set(key, value, flags, expiration) 255 | } 256 | 257 | fn add(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 258 | let server = self.find_server_by_key(key); 259 | server.borrow_mut().proto.add(key, value, flags, expiration) 260 | } 261 | 262 | fn delete(&mut self, key: &[u8]) -> MemCachedResult<()> { 263 | let server = self.find_server_by_key(key); 264 | server.borrow_mut().proto.delete(key) 265 | } 266 | 267 | fn replace(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 268 | let server = self.find_server_by_key(key); 269 | server.borrow_mut().proto.replace(key, value, flags, expiration) 270 | } 271 | 272 | fn get(&mut self, key: &[u8]) -> MemCachedResult<(Vec, u32)> { 273 | let server = self.find_server_by_key(key); 274 | server.borrow_mut().proto.get(key) 275 | } 276 | 277 | fn getk(&mut self, key: &[u8]) -> MemCachedResult<(Vec, Vec, u32)> { 278 | let server = self.find_server_by_key(key); 279 | server.borrow_mut().proto.getk(key) 280 | } 281 | 282 | fn increment(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult { 283 | let server = self.find_server_by_key(key); 284 | server.borrow_mut().proto.increment(key, amount, initial, expiration) 285 | } 286 | 287 | fn decrement(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult { 288 | let server = self.find_server_by_key(key); 289 | server.borrow_mut().proto.increment(key, amount, initial, expiration) 290 | } 291 | 292 | fn append(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()> { 293 | let server = self.find_server_by_key(key); 294 | server.borrow_mut().proto.append(key, value) 295 | } 296 | 297 | fn prepend(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()> { 298 | let server = self.find_server_by_key(key); 299 | server.borrow_mut().proto.prepend(key, value) 300 | } 301 | 302 | fn touch(&mut self, key: &[u8], expiration: u32) -> MemCachedResult<()> { 303 | let server = self.find_server_by_key(key); 304 | server.borrow_mut().proto.touch(key, expiration) 305 | } 306 | } 307 | 308 | impl NoReplyOperation for Client { 309 | fn set_noreply(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 310 | let server = self.find_server_by_key(key); 311 | server.borrow_mut().proto.set_noreply(key, value, flags, expiration) 312 | } 313 | 314 | fn add_noreply(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 315 | let server = self.find_server_by_key(key); 316 | server.borrow_mut().proto.add_noreply(key, value, flags, expiration) 317 | } 318 | 319 | fn delete_noreply(&mut self, key: &[u8]) -> MemCachedResult<()> { 320 | let server = self.find_server_by_key(key); 321 | server.borrow_mut().proto.delete_noreply(key) 322 | } 323 | 324 | fn replace_noreply(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 325 | let server = self.find_server_by_key(key); 326 | server.borrow_mut().proto.replace_noreply(key, value, flags, expiration) 327 | } 328 | 329 | fn increment_noreply(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult<()> { 330 | let server = self.find_server_by_key(key); 331 | server 332 | .borrow_mut() 333 | .proto 334 | .increment_noreply(key, amount, initial, expiration) 335 | } 336 | 337 | fn decrement_noreply(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult<()> { 338 | let server = self.find_server_by_key(key); 339 | server 340 | .borrow_mut() 341 | .proto 342 | .decrement_noreply(key, amount, initial, expiration) 343 | } 344 | 345 | fn append_noreply(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()> { 346 | let server = self.find_server_by_key(key); 347 | server.borrow_mut().proto.append_noreply(key, value) 348 | } 349 | 350 | fn prepend_noreply(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()> { 351 | let server = self.find_server_by_key(key); 352 | server.borrow_mut().proto.prepend_noreply(key, value) 353 | } 354 | } 355 | 356 | impl CasOperation for Client { 357 | fn set_cas(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32, cas: u64) -> MemCachedResult { 358 | let server = self.find_server_by_key(key); 359 | server.borrow_mut().proto.set_cas(key, value, flags, expiration, cas) 360 | } 361 | 362 | fn add_cas(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult { 363 | let server = self.find_server_by_key(key); 364 | server.borrow_mut().proto.add_cas(key, value, flags, expiration) 365 | } 366 | 367 | fn replace_cas(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32, cas: u64) -> MemCachedResult { 368 | let server = self.find_server_by_key(key); 369 | server 370 | .borrow_mut() 371 | .proto 372 | .replace_cas(key, value, flags, expiration, cas) 373 | } 374 | 375 | fn get_cas(&mut self, key: &[u8]) -> MemCachedResult<(Vec, u32, u64)> { 376 | let server = self.find_server_by_key(key); 377 | server.borrow_mut().proto.get_cas(key) 378 | } 379 | 380 | fn getk_cas(&mut self, key: &[u8]) -> MemCachedResult<(Vec, Vec, u32, u64)> { 381 | let server = self.find_server_by_key(key); 382 | server.borrow_mut().proto.getk_cas(key) 383 | } 384 | 385 | fn increment_cas( 386 | &mut self, 387 | key: &[u8], 388 | amount: u64, 389 | initial: u64, 390 | expiration: u32, 391 | cas: u64, 392 | ) -> MemCachedResult<(u64, u64)> { 393 | let server = self.find_server_by_key(key); 394 | server 395 | .borrow_mut() 396 | .proto 397 | .increment_cas(key, amount, initial, expiration, cas) 398 | } 399 | 400 | fn decrement_cas( 401 | &mut self, 402 | key: &[u8], 403 | amount: u64, 404 | initial: u64, 405 | expiration: u32, 406 | cas: u64, 407 | ) -> MemCachedResult<(u64, u64)> { 408 | let server = self.find_server_by_key(key); 409 | server 410 | .borrow_mut() 411 | .proto 412 | .decrement_cas(key, amount, initial, expiration, cas) 413 | } 414 | 415 | fn append_cas(&mut self, key: &[u8], value: &[u8], cas: u64) -> MemCachedResult { 416 | let server = self.find_server_by_key(key); 417 | server.borrow_mut().proto.append_cas(key, value, cas) 418 | } 419 | 420 | fn prepend_cas(&mut self, key: &[u8], value: &[u8], cas: u64) -> MemCachedResult { 421 | let server = self.find_server_by_key(key); 422 | server.borrow_mut().proto.prepend_cas(key, value, cas) 423 | } 424 | 425 | fn touch_cas(&mut self, key: &[u8], expiration: u32, cas: u64) -> MemCachedResult { 426 | let server = self.find_server_by_key(key); 427 | server.borrow_mut().proto.touch_cas(key, expiration, cas) 428 | } 429 | } 430 | 431 | impl MultiOperation for Client { 432 | fn set_multi(&mut self, kv: BTreeMap<&[u8], (&[u8], u32, u32)>) -> MemCachedResult<()> { 433 | assert!(kv.keys().len() > 1); 434 | assert_eq!(self.servers.len(), 1); 435 | let server = self.find_server_by_key(kv.keys().next().unwrap()); 436 | server.borrow_mut().proto.set_multi(kv) 437 | } 438 | fn delete_multi(&mut self, keys: &[&[u8]]) -> MemCachedResult<()> { 439 | assert!(keys.len() > 1); 440 | assert_eq!(self.servers.len(), 1); 441 | let server = self.find_server_by_key(keys[0]); 442 | server.borrow_mut().proto.delete_multi(keys) 443 | } 444 | fn increment_multi<'a>( 445 | &mut self, 446 | kv: HashMap<&'a [u8], (u64, u64, u32)>, 447 | ) -> MemCachedResult> { 448 | assert!(kv.keys().len() > 1); 449 | assert_eq!(self.servers.len(), 1); 450 | let server = self.find_server_by_key(kv.keys().next().unwrap()); 451 | server.borrow_mut().proto.increment_multi(kv) 452 | } 453 | fn get_multi(&mut self, keys: &[&[u8]]) -> MemCachedResult, (Vec, u32)>> { 454 | assert!(keys.len() > 1); 455 | assert_eq!(self.servers.len(), 1); 456 | let server = self.find_server_by_key(keys[0]); 457 | server.borrow_mut().proto.get_multi(keys) 458 | } 459 | } 460 | 461 | #[cfg(all(test, feature = "nightly"))] 462 | mod bench_test { 463 | use super::Client; 464 | use crate::proto::{NoReplyOperation, Operation, ProtoType}; 465 | use test::Bencher; 466 | 467 | fn generate_data(len: usize) -> Vec { 468 | (0..len).map(|_| fastrand::u8(..)).collect() 469 | } 470 | 471 | #[bench] 472 | fn bench_set_64(b: &mut Bencher) { 473 | let key = b"test:test_bench"; 474 | let val = generate_data(64); 475 | 476 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 477 | 478 | b.iter(|| client.set(key, &val[..], 0, 2)); 479 | } 480 | 481 | #[bench] 482 | fn bench_set_noreply_64(b: &mut Bencher) { 483 | let key = b"test:test_bench"; 484 | let val = generate_data(64); 485 | 486 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 487 | 488 | b.iter(|| client.set_noreply(key, &val[..], 0, 2)); 489 | } 490 | 491 | #[bench] 492 | fn bench_set_512(b: &mut Bencher) { 493 | let key = b"test:test_bench"; 494 | let val = generate_data(512); 495 | 496 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 497 | 498 | b.iter(|| client.set(key, &val[..], 0, 2)); 499 | } 500 | 501 | #[bench] 502 | fn bench_set_noreply_512(b: &mut Bencher) { 503 | let key = b"test:test_bench"; 504 | let val = generate_data(512); 505 | 506 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 507 | 508 | b.iter(|| client.set_noreply(key, &val[..], 0, 2)); 509 | } 510 | 511 | #[bench] 512 | fn bench_set_1024(b: &mut Bencher) { 513 | let key = b"test:test_bench"; 514 | let val = generate_data(1024); 515 | 516 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 517 | 518 | b.iter(|| client.set(key, &val[..], 0, 2)); 519 | } 520 | 521 | #[bench] 522 | fn bench_set_noreply_1024(b: &mut Bencher) { 523 | let key = b"test:test_bench"; 524 | let val = generate_data(1024); 525 | 526 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 527 | 528 | b.iter(|| client.set_noreply(key, &val[..], 0, 2)); 529 | } 530 | 531 | #[bench] 532 | fn bench_set_4096(b: &mut Bencher) { 533 | let key = b"test:test_bench"; 534 | let val = generate_data(4096); 535 | 536 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 537 | 538 | b.iter(|| client.set(key, &val[..], 0, 2)); 539 | } 540 | 541 | #[bench] 542 | fn bench_set_noreply_4096(b: &mut Bencher) { 543 | let key = b"test:test_bench"; 544 | let val = generate_data(4096); 545 | 546 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 547 | 548 | b.iter(|| client.set_noreply(key, &val[..], 0, 2)); 549 | } 550 | 551 | #[bench] 552 | fn bench_set_16384(b: &mut Bencher) { 553 | let key = b"test:test_bench"; 554 | let val = generate_data(16384); 555 | 556 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 557 | 558 | b.iter(|| client.set(key, &val[..], 0, 2)); 559 | } 560 | 561 | #[bench] 562 | fn bench_set_noreply_16384(b: &mut Bencher) { 563 | let key = b"test:test_bench"; 564 | let val = generate_data(16384); 565 | 566 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 567 | 568 | b.iter(|| client.set_noreply(key, &val[..], 0, 2)); 569 | } 570 | } 571 | 572 | #[cfg(test)] 573 | mod test { 574 | use super::Client; 575 | use crate::proto::{ProtoType, MultiOperation}; 576 | use std::collections::{BTreeMap, HashMap}; 577 | 578 | #[test] 579 | fn test_set_multi() { 580 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 581 | 582 | let mut data = BTreeMap::new(); 583 | data.insert(&b"test:set_multi_hello1"[..], (&b"world1"[..], 0xdead_beef, 120)); 584 | data.insert(&b"test:set_multi_hello2"[..], (&b"world2"[..], 0xdead_beef, 120)); 585 | 586 | client.set_multi(data).unwrap(); 587 | } 588 | 589 | #[test] 590 | #[should_panic] 591 | fn test_set_multi_panic_with_no_keys() { 592 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 593 | let data = BTreeMap::new(); 594 | 595 | client.set_multi(data).unwrap(); 596 | } 597 | 598 | #[test] 599 | fn test_delete_multi() { 600 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 601 | 602 | client.delete_multi(&[b"test:delete_multi_hello1", b"test:delete_multi_hello2"]).unwrap(); 603 | } 604 | 605 | #[test] 606 | #[should_panic] 607 | fn test_delete_multi_panic_with_no_keys() { 608 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 609 | 610 | client.delete_multi(&[]).unwrap(); 611 | } 612 | 613 | #[test] 614 | fn test_increment_multi() { 615 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 616 | 617 | let mut data = HashMap::new(); 618 | data.insert(&b"test:increment_multi_num1"[..], (10, 50, 120)); 619 | data.insert(&b"test:increment_multi_num2"[..], (20, 50, 120)); 620 | 621 | client.increment_multi(data).unwrap(); 622 | } 623 | 624 | #[test] 625 | #[should_panic] 626 | fn test_increment_multi_panic_with_no_keys() { 627 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 628 | 629 | client.increment_multi(HashMap::new()).unwrap(); 630 | } 631 | 632 | #[test] 633 | fn test_get_multi() { 634 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 635 | 636 | client.get_multi(&[b"test:get_multi_hello1", b"test:get_multi_hello2"]).unwrap(); 637 | } 638 | 639 | #[test] 640 | #[should_panic] 641 | fn test_get_multi_panic_with_no_keys() { 642 | let mut client = Client::connect(&[("tcp://127.0.0.1:11211", 1)], ProtoType::Binary).unwrap(); 643 | 644 | client.get_multi(&[]).unwrap(); 645 | } 646 | } 647 | -------------------------------------------------------------------------------- /src/proto/binarydef.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Y. T. Chung 2 | // Licensed under the Apache License, Version 2.0 3 | // or the MIT 5 | // license , 6 | // at your option. All files in the project carrying such 7 | // notice may not be copied, modified, or distributed except 8 | // according to those terms. 9 | 10 | //! This module is for serializing binary packet 11 | //! 12 | //! The protocol specification is defined in 13 | //! [BinaryProtocolRevamped](https://code.google.com/p/memcached/wiki/BinaryProtocolRevamped) 14 | //! 15 | // General format of a packet: 16 | // 17 | // Byte/ 0 | 1 | 2 | 3 | 18 | // / | | | | 19 | // |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| 20 | // +---------------+---------------+---------------+---------------+ 21 | // 0/ HEADER / 22 | // / / 23 | // / / 24 | // / / 25 | // +---------------+---------------+---------------+---------------+ 26 | // 24/ COMMAND-SPECIFIC EXTRAS (as needed) / 27 | // +/ (note length in the extras length header field) / 28 | // +---------------+---------------+---------------+---------------+ 29 | // m/ Key (as needed) / 30 | // +/ (note length in key length header field) / 31 | // +---------------+---------------+---------------+---------------+ 32 | // n/ Value (as needed) / 33 | // +/ (note length is total body length header field, minus / 34 | // +/ sum of the extras and key length body fields) / 35 | // +---------------+---------------+---------------+---------------+ 36 | // Total 24 bytes 37 | 38 | #![allow(dead_code)] 39 | #![allow(clippy::too_many_arguments)] 40 | 41 | use std::io::{self, Read, Write}; 42 | 43 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 44 | use bytes::{Bytes, BytesMut}; 45 | 46 | #[rustfmt::skip] 47 | mod consts { 48 | pub const MAGIC_REQUEST: u8 = 0x80; 49 | pub const MAGIC_RESPONSE: u8 = 0x81; 50 | 51 | pub const STATUS_NO_ERROR: u16 = 0x0000; 52 | pub const STATUS_KEY_NOT_FOUND: u16 = 0x0001; 53 | pub const STATUS_KEY_EXISTS: u16 = 0x0002; 54 | pub const STATUS_VALUE_TOO_LARGE: u16 = 0x0003; 55 | pub const STATUS_INVALID_ARGUMENTS: u16 = 0x0004; 56 | pub const STATUS_ITEM_NOT_STORED: u16 = 0x0005; 57 | pub const STATUS_INCR_OR_DECR_ON_NON_NUMERIC_VALUE: u16 = 0x0006; 58 | pub const STATUS_VBUCKET_BELONGS_TO_OTHER_SERVER: u16 = 0x0007; 59 | pub const STATUS_AUTHENTICATION_ERROR: u16 = 0x0008; 60 | pub const STATUS_AUTHENTICATION_CONTINUE: u16 = 0x0009; 61 | pub const STATUS_UNKNOWN_COMMAND: u16 = 0x0081; 62 | pub const STATUS_OUT_OF_MEMORY: u16 = 0x0082; 63 | pub const STATUS_NOT_SUPPORTED: u16 = 0x0083; 64 | pub const STATUS_INTERNAL_ERROR: u16 = 0x0084; 65 | pub const STATUS_BUSY: u16 = 0x0085; 66 | pub const STATUS_TEMPORARY_FAILURE: u16 = 0x0086; 67 | pub const STATUS_AUTHENTICATION_REQUIRED: u16 = 0x0020; 68 | pub const STATUS_AUTHENTICATION_FURTHER_STEP_REQUIRED: u16 = 0x0021; 69 | 70 | pub const OPCODE_GET: u8 = 0x00; 71 | pub const OPCODE_SET: u8 = 0x01; 72 | pub const OPCODE_ADD: u8 = 0x02; 73 | pub const OPCODE_REPLACE: u8 = 0x03; 74 | pub const OPCODE_DEL: u8 = 0x04; 75 | pub const OPCODE_INCR: u8 = 0x05; 76 | pub const OPCODE_DECR: u8 = 0x06; 77 | pub const OPCODE_QUIT: u8 = 0x07; 78 | pub const OPCODE_FLUSH: u8 = 0x08; 79 | pub const OPCODE_GETQ: u8 = 0x09; 80 | pub const OPCODE_NOP: u8 = 0x0A; 81 | pub const OPCODE_VERSION: u8 = 0x0B; 82 | pub const OPCODE_GETK: u8 = 0x0C; 83 | pub const OPCODE_GETKQ: u8 = 0x0D; 84 | pub const OPCODE_APPEND: u8 = 0x0E; 85 | pub const OPCODE_PREPEND: u8 = 0x0F; 86 | pub const OPCODE_STAT: u8 = 0x10; 87 | pub const OPCODE_SETQ: u8 = 0x11; 88 | pub const OPCODE_ADDQ: u8 = 0x12; 89 | pub const OPCODE_REPLACEQ: u8 = 0x13; 90 | pub const OPCODE_DELQ: u8 = 0x14; 91 | pub const OPCODE_INCRQ: u8 = 0x15; 92 | pub const OPCODE_DECRQ: u8 = 0x16; 93 | pub const OPCODE_QUITQ: u8 = 0x17; 94 | pub const OPCODE_FLUSHQ: u8 = 0x18; 95 | pub const OPCODE_APPENDQ: u8 = 0x19; 96 | pub const OPCODE_PREPENDQ: u8 = 0x1A; 97 | pub const OPCODE_VERBOSITY: u8 = 0x1B; 98 | pub const OPCODE_TOUCH: u8 = 0x1C; 99 | pub const OPCODE_GAT: u8 = 0x1D; 100 | pub const OPCODE_GATQ: u8 = 0x1E; 101 | pub const OPCODE_SASL_LIST_MECHS: u8 = 0x20; 102 | pub const OPCODE_SASL_AUTH: u8 = 0x21; 103 | pub const OPCODE_SASL_STEP: u8 = 0x22; 104 | pub const OPCODE_RGET: u8 = 0x30; 105 | pub const OPCODE_RSET: u8 = 0x31; 106 | pub const OPCODE_RSETQ: u8 = 0x32; 107 | pub const OPCODE_RAPPEND: u8 = 0x33; 108 | pub const OPCODE_RAPPENDQ: u8 = 0x34; 109 | pub const OPCODE_RPREPEND: u8 = 0x35; 110 | pub const OPCODE_RPREPENDQ: u8 = 0x36; 111 | pub const OPCODE_RDEL: u8 = 0x37; 112 | pub const OPCODE_RDELQ: u8 = 0x38; 113 | pub const OPCODE_RINCR: u8 = 0x39; 114 | pub const OPCODE_RINCRQ: u8 = 0x3A; 115 | pub const OPCODE_RDECR: u8 = 0x3B; 116 | pub const OPCODE_RDECRQ: u8 = 0x3C; 117 | pub const OPCODE_SET_VBUCKET: u8 = 0x3D; 118 | pub const OPCODE_GET_VBUCKET: u8 = 0x3E; 119 | pub const OPCODE_DEL_VBUCKET: u8 = 0x3F; 120 | pub const OPCODE_TAP_CONNECT: u8 = 0x40; 121 | pub const OPCODE_TAP_MUTATION: u8 = 0x41; 122 | pub const OPCODE_TAP_DEL: u8 = 0x42; 123 | pub const OPCODE_TAP_FLUSH: u8 = 0x43; 124 | pub const OPCODE_TAP_OPAQUE: u8 = 0x44; 125 | pub const OPCODE_TAP_VBUCKET_SET: u8 = 0x45; 126 | pub const OPCODE_TAP_CHECKPOINT_START: u8 = 0x46; 127 | pub const OPCODE_TAP_CHECKPOINT_END: u8 = 0x47; 128 | 129 | pub const DATA_TYPE_RAW_BYTES: u8 = 0x00; 130 | } 131 | 132 | /// Memcached response status 133 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 134 | #[repr(u16)] 135 | #[rustfmt::skip] 136 | pub enum Status { 137 | NoError = consts::STATUS_NO_ERROR, 138 | KeyNotFound = consts::STATUS_KEY_NOT_FOUND, 139 | KeyExists = consts::STATUS_KEY_EXISTS, 140 | ValueTooLarge = consts::STATUS_VALUE_TOO_LARGE, 141 | InvalidArguments = consts::STATUS_INVALID_ARGUMENTS, 142 | ItemNotStored = consts::STATUS_ITEM_NOT_STORED, 143 | IncrDecrOnNonNumericValue = consts::STATUS_INCR_OR_DECR_ON_NON_NUMERIC_VALUE, 144 | VBucketBelongsToOtherServer = consts::STATUS_VBUCKET_BELONGS_TO_OTHER_SERVER, 145 | AuthenticationError = consts::STATUS_AUTHENTICATION_ERROR, 146 | AuthenticationContinue = consts::STATUS_AUTHENTICATION_CONTINUE, 147 | UnknownCommand = consts::STATUS_UNKNOWN_COMMAND, 148 | OutOfMemory = consts::STATUS_OUT_OF_MEMORY, 149 | NotSupported = consts::STATUS_NOT_SUPPORTED, 150 | InternalError = consts::STATUS_INTERNAL_ERROR, 151 | Busy = consts::STATUS_BUSY, 152 | TemporaryFailure = consts::STATUS_TEMPORARY_FAILURE, 153 | AuthenticationRequired = consts::STATUS_AUTHENTICATION_REQUIRED, 154 | AuthenticationFurtherStepRequired = consts::STATUS_AUTHENTICATION_FURTHER_STEP_REQUIRED, 155 | } 156 | 157 | impl Status { 158 | /// Get the binary code of the status 159 | #[inline] 160 | pub fn to_u16(self) -> u16 { 161 | self as u16 162 | } 163 | 164 | /// Generate a Status from binary code 165 | #[inline] 166 | #[rustfmt::skip] 167 | pub fn from_u16(code: u16) -> Option { 168 | match code { 169 | consts::STATUS_NO_ERROR => Some(Status::NoError), 170 | consts::STATUS_KEY_NOT_FOUND => Some(Status::KeyNotFound), 171 | consts::STATUS_KEY_EXISTS => Some(Status::KeyExists), 172 | consts::STATUS_VALUE_TOO_LARGE => Some(Status::ValueTooLarge), 173 | consts::STATUS_INVALID_ARGUMENTS => Some(Status::InvalidArguments), 174 | consts::STATUS_ITEM_NOT_STORED => Some(Status::ItemNotStored), 175 | consts::STATUS_INCR_OR_DECR_ON_NON_NUMERIC_VALUE => Some(Status::IncrDecrOnNonNumericValue), 176 | consts::STATUS_VBUCKET_BELONGS_TO_OTHER_SERVER => Some(Status::VBucketBelongsToOtherServer), 177 | consts::STATUS_AUTHENTICATION_ERROR => Some(Status::AuthenticationError), 178 | consts::STATUS_AUTHENTICATION_CONTINUE => Some(Status::AuthenticationContinue), 179 | consts::STATUS_UNKNOWN_COMMAND => Some(Status::UnknownCommand), 180 | consts::STATUS_OUT_OF_MEMORY => Some(Status::OutOfMemory), 181 | consts::STATUS_NOT_SUPPORTED => Some(Status::NotSupported), 182 | consts::STATUS_INTERNAL_ERROR => Some(Status::InternalError), 183 | consts::STATUS_BUSY => Some(Status::Busy), 184 | consts::STATUS_TEMPORARY_FAILURE => Some(Status::TemporaryFailure), 185 | consts::STATUS_AUTHENTICATION_REQUIRED => Some(Status::AuthenticationRequired), 186 | consts::STATUS_AUTHENTICATION_FURTHER_STEP_REQUIRED => Some(Status::AuthenticationFurtherStepRequired), 187 | _ => None, 188 | } 189 | } 190 | 191 | /// Get a short description 192 | #[inline] 193 | #[rustfmt::skip] 194 | pub fn desc(self) -> &'static str { 195 | match self { 196 | Status::NoError => "no error", 197 | Status::KeyNotFound => "key not found", 198 | Status::KeyExists => "key exists", 199 | Status::ValueTooLarge => "value too large", 200 | Status::InvalidArguments => "invalid argument", 201 | Status::ItemNotStored => "item not stored", 202 | Status::IncrDecrOnNonNumericValue => "incr or decr on non-numeric value", 203 | Status::VBucketBelongsToOtherServer => "vbucket belongs to other server", 204 | Status::AuthenticationError => "authentication error", 205 | Status::AuthenticationContinue => "authentication continue", 206 | Status::UnknownCommand => "unknown command", 207 | Status::OutOfMemory => "out of memory", 208 | Status::NotSupported => "not supported", 209 | Status::InternalError => "internal error", 210 | Status::Busy => "busy", 211 | Status::TemporaryFailure => "temporary failure", 212 | Status::AuthenticationRequired => "authentication required/not successful", 213 | Status::AuthenticationFurtherStepRequired => "further authentication steps required", 214 | } 215 | } 216 | } 217 | 218 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 219 | #[repr(u8)] 220 | #[rustfmt::skip] 221 | pub enum Command { 222 | Get = consts::OPCODE_GET, 223 | Set = consts::OPCODE_SET, 224 | Add = consts::OPCODE_ADD, 225 | Replace = consts::OPCODE_REPLACE, 226 | Delete = consts::OPCODE_DEL, 227 | Increment = consts::OPCODE_INCR, 228 | Decrement = consts::OPCODE_DECR, 229 | Quit = consts::OPCODE_QUIT, 230 | Flush = consts::OPCODE_FLUSH, 231 | GetQuietly = consts::OPCODE_GETQ, 232 | Noop = consts::OPCODE_NOP, 233 | Version = consts::OPCODE_VERSION, 234 | GetKey = consts::OPCODE_GETK, 235 | GetKeyQuietly = consts::OPCODE_GETKQ, 236 | Append = consts::OPCODE_APPEND, 237 | Prepend = consts::OPCODE_PREPEND, 238 | Stat = consts::OPCODE_STAT, 239 | SetQuietly = consts::OPCODE_SETQ, 240 | AddQuietly = consts::OPCODE_ADDQ, 241 | ReplaceQuietly = consts::OPCODE_REPLACEQ, 242 | DeleteQuietly = consts::OPCODE_DELQ, 243 | IncrementQuietly = consts::OPCODE_INCRQ, 244 | DecrementQuietly = consts::OPCODE_DECRQ, 245 | QuitQuietly = consts::OPCODE_QUITQ, 246 | FlushQuietly = consts::OPCODE_FLUSHQ, 247 | AppendQuietly = consts::OPCODE_APPENDQ, 248 | PrependQuietly = consts::OPCODE_PREPENDQ, 249 | Verbosity = consts::OPCODE_VERBOSITY, 250 | Touch = consts::OPCODE_TOUCH, 251 | GetAndTouch = consts::OPCODE_GAT, 252 | GetAndTouchQuietly = consts::OPCODE_GATQ, 253 | SaslListMechanisms = consts::OPCODE_SASL_LIST_MECHS, 254 | SaslAuthenticate = consts::OPCODE_SASL_AUTH, 255 | SaslStep = consts::OPCODE_SASL_STEP, 256 | RGet = consts::OPCODE_RGET, 257 | RSet = consts::OPCODE_RSET, 258 | RSetQuietly = consts::OPCODE_RSETQ, 259 | RAppend = consts::OPCODE_RAPPEND, 260 | RAppendQuietly = consts::OPCODE_RAPPENDQ, 261 | RPrepend = consts::OPCODE_RPREPEND, 262 | RPrependQuietly = consts::OPCODE_RPREPENDQ, 263 | RDelete = consts::OPCODE_RDEL, 264 | RDeleteQuietly = consts::OPCODE_RDELQ, 265 | RIncrement = consts::OPCODE_RINCR, 266 | RIncrementQuietly = consts::OPCODE_RINCRQ, 267 | RDecrement = consts::OPCODE_RDECR, 268 | RDecrementQuietly = consts::OPCODE_RDECRQ, 269 | SetVBucket = consts::OPCODE_SET_VBUCKET, 270 | GetVBucket = consts::OPCODE_GET_VBUCKET, 271 | DelVBucket = consts::OPCODE_DEL_VBUCKET, 272 | TapConnect = consts::OPCODE_TAP_CONNECT, 273 | TapMutation = consts::OPCODE_TAP_MUTATION, 274 | TapDelete = consts::OPCODE_TAP_DEL, 275 | TapFlush = consts::OPCODE_TAP_FLUSH, 276 | TapOpaque = consts::OPCODE_TAP_OPAQUE, 277 | TapVBucketSet = consts::OPCODE_TAP_VBUCKET_SET, 278 | TapCheckpointStart = consts::OPCODE_TAP_CHECKPOINT_START, 279 | TapCheckpointEnd = consts::OPCODE_TAP_CHECKPOINT_END, 280 | } 281 | 282 | impl Command { 283 | #[inline] 284 | fn to_u8(self) -> u8 { 285 | self as u8 286 | } 287 | 288 | #[inline] 289 | #[rustfmt::skip] 290 | fn from_u8(code: u8) -> Option { 291 | match code { 292 | consts::OPCODE_GET => Some(Command::Get), 293 | consts::OPCODE_SET => Some(Command::Set), 294 | consts::OPCODE_ADD => Some(Command::Add), 295 | consts::OPCODE_REPLACE => Some(Command::Replace), 296 | consts::OPCODE_DEL => Some(Command::Delete), 297 | consts::OPCODE_INCR => Some(Command::Increment), 298 | consts::OPCODE_DECR => Some(Command::Decrement), 299 | consts::OPCODE_QUIT => Some(Command::Quit), 300 | consts::OPCODE_FLUSH => Some(Command::Flush), 301 | consts::OPCODE_GETQ => Some(Command::GetQuietly), 302 | consts::OPCODE_NOP => Some(Command::Noop), 303 | consts::OPCODE_VERSION => Some(Command::Version), 304 | consts::OPCODE_GETK => Some(Command::GetKey), 305 | consts::OPCODE_GETKQ => Some(Command::GetKeyQuietly), 306 | consts::OPCODE_APPEND => Some(Command::Append), 307 | consts::OPCODE_PREPEND => Some(Command::Prepend), 308 | consts::OPCODE_STAT => Some(Command::Stat), 309 | consts::OPCODE_SETQ => Some(Command::SetQuietly), 310 | consts::OPCODE_ADDQ => Some(Command::AddQuietly), 311 | consts::OPCODE_REPLACEQ => Some(Command::ReplaceQuietly), 312 | consts::OPCODE_DELQ => Some(Command::DeleteQuietly), 313 | consts::OPCODE_INCRQ => Some(Command::IncrementQuietly), 314 | consts::OPCODE_DECRQ => Some(Command::DecrementQuietly), 315 | consts::OPCODE_QUITQ => Some(Command::QuitQuietly), 316 | consts::OPCODE_FLUSHQ => Some(Command::FlushQuietly), 317 | consts::OPCODE_APPENDQ => Some(Command::AppendQuietly), 318 | consts::OPCODE_PREPENDQ => Some(Command::PrependQuietly), 319 | consts::OPCODE_VERBOSITY => Some(Command::Verbosity), 320 | consts::OPCODE_TOUCH => Some(Command::Touch), 321 | consts::OPCODE_GAT => Some(Command::GetAndTouch), 322 | consts::OPCODE_GATQ => Some(Command::GetAndTouchQuietly), 323 | consts::OPCODE_SASL_LIST_MECHS => Some(Command::SaslListMechanisms), 324 | consts::OPCODE_SASL_AUTH => Some(Command::SaslAuthenticate), 325 | consts::OPCODE_SASL_STEP => Some(Command::SaslStep), 326 | consts::OPCODE_RGET => Some(Command::RGet), 327 | consts::OPCODE_RSET => Some(Command::RSet), 328 | consts::OPCODE_RSETQ => Some(Command::RSetQuietly), 329 | consts::OPCODE_RAPPEND => Some(Command::RAppend), 330 | consts::OPCODE_RAPPENDQ => Some(Command::RAppendQuietly), 331 | consts::OPCODE_RPREPEND => Some(Command::RPrepend), 332 | consts::OPCODE_RPREPENDQ => Some(Command::RPrependQuietly), 333 | consts::OPCODE_RDEL => Some(Command::RDelete), 334 | consts::OPCODE_RDELQ => Some(Command::RDeleteQuietly), 335 | consts::OPCODE_RINCR => Some(Command::RIncrement), 336 | consts::OPCODE_RINCRQ => Some(Command::RIncrementQuietly), 337 | consts::OPCODE_RDECR => Some(Command::RDecrement), 338 | consts::OPCODE_RDECRQ => Some(Command::RDecrementQuietly), 339 | consts::OPCODE_SET_VBUCKET => Some(Command::SetVBucket), 340 | consts::OPCODE_GET_VBUCKET => Some(Command::GetVBucket), 341 | consts::OPCODE_DEL_VBUCKET => Some(Command::DelVBucket), 342 | consts::OPCODE_TAP_CONNECT => Some(Command::TapConnect), 343 | consts::OPCODE_TAP_MUTATION => Some(Command::TapMutation), 344 | consts::OPCODE_TAP_DEL => Some(Command::TapDelete), 345 | consts::OPCODE_TAP_FLUSH => Some(Command::TapFlush), 346 | consts::OPCODE_TAP_OPAQUE => Some(Command::TapOpaque), 347 | consts::OPCODE_TAP_VBUCKET_SET => Some(Command::TapVBucketSet), 348 | consts::OPCODE_TAP_CHECKPOINT_START => Some(Command::TapCheckpointStart), 349 | consts::OPCODE_TAP_CHECKPOINT_END => Some(Command::TapCheckpointEnd), 350 | _ => None, 351 | } 352 | } 353 | } 354 | 355 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 356 | pub enum DataType { 357 | RawBytes, 358 | } 359 | 360 | impl DataType { 361 | #[inline] 362 | fn to_u8(self) -> u8 { 363 | match self { 364 | DataType::RawBytes => consts::DATA_TYPE_RAW_BYTES, 365 | } 366 | } 367 | 368 | #[inline] 369 | fn from_u8(code: u8) -> Option { 370 | match code { 371 | consts::DATA_TYPE_RAW_BYTES => Some(DataType::RawBytes), 372 | _ => None, 373 | } 374 | } 375 | } 376 | 377 | // Byte/ 0 | 1 | 2 | 3 | 378 | // / | | | | 379 | // |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| 380 | // +---------------+---------------+---------------+---------------+ 381 | // 0| Magic | Opcode | Key length | 382 | // +---------------+---------------+---------------+---------------+ 383 | // 4| Extras length | Data type | vbucket id | 384 | // +---------------+---------------+---------------+---------------+ 385 | // 8| Total body length | 386 | // +---------------+---------------+---------------+---------------+ 387 | // 12| Opaque | 388 | // +---------------+---------------+---------------+---------------+ 389 | // 16| CAS | 390 | // | | 391 | // +---------------+---------------+---------------+---------------+ 392 | // Total 24 bytes 393 | #[derive(Clone, Debug)] 394 | pub struct RequestHeader { 395 | pub command: Command, 396 | key_len: u16, 397 | extra_len: u8, 398 | pub data_type: DataType, 399 | pub vbucket_id: u16, 400 | body_len: u32, 401 | pub opaque: u32, 402 | pub cas: u64, 403 | } 404 | 405 | impl RequestHeader { 406 | pub fn new( 407 | cmd: Command, 408 | dtype: DataType, 409 | vbid: u16, 410 | opaque: u32, 411 | cas: u64, 412 | key_len: u16, 413 | extra_len: u8, 414 | body_len: u32, 415 | ) -> RequestHeader { 416 | RequestHeader { 417 | command: cmd, 418 | key_len, 419 | extra_len, 420 | data_type: dtype, 421 | vbucket_id: vbid, 422 | body_len, 423 | opaque, 424 | cas, 425 | } 426 | } 427 | 428 | pub fn from_payload( 429 | cmd: Command, 430 | dtype: DataType, 431 | vbid: u16, 432 | opaque: u32, 433 | cas: u64, 434 | key: &[u8], 435 | extra: &[u8], 436 | value: &[u8], 437 | ) -> RequestHeader { 438 | let key_len = key.len() as u16; 439 | let extra_len = extra.len() as u8; 440 | let body_len = (key.len() + extra.len() + value.len()) as u32; 441 | 442 | RequestHeader::new(cmd, dtype, vbid, opaque, cas, key_len, extra_len, body_len) 443 | } 444 | 445 | #[inline] 446 | pub fn write_to(&self, writer: &mut W) -> io::Result<()> { 447 | writer.write_u8(consts::MAGIC_REQUEST)?; 448 | writer.write_u8(self.command.to_u8())?; 449 | writer.write_u16::(self.key_len)?; 450 | writer.write_u8(self.extra_len)?; 451 | writer.write_u8(self.data_type.to_u8())?; 452 | writer.write_u16::(self.vbucket_id)?; 453 | writer.write_u32::(self.body_len)?; 454 | writer.write_u32::(self.opaque)?; 455 | writer.write_u64::(self.cas)?; 456 | 457 | Ok(()) 458 | } 459 | 460 | #[inline] 461 | pub fn read_from(reader: &mut R) -> io::Result { 462 | let magic = reader.read_u8()?; 463 | 464 | if magic != consts::MAGIC_REQUEST { 465 | return Err(io::Error::new(io::ErrorKind::Other, "Invalid magic")); 466 | } 467 | 468 | Ok(RequestHeader { 469 | command: match Command::from_u8(reader.read_u8()?) { 470 | Some(c) => c, 471 | None => return Err(io::Error::new(io::ErrorKind::Other, "Invalid command")), 472 | }, 473 | key_len: reader.read_u16::()?, 474 | extra_len: reader.read_u8()?, 475 | data_type: match DataType::from_u8(reader.read_u8()?) { 476 | Some(d) => d, 477 | None => return Err(io::Error::new(io::ErrorKind::Other, "Invalid data type")), 478 | }, 479 | vbucket_id: reader.read_u16::()?, 480 | body_len: reader.read_u32::()?, 481 | opaque: reader.read_u32::()?, 482 | cas: reader.read_u64::()?, 483 | }) 484 | } 485 | } 486 | 487 | // Byte/ 0 | 1 | 2 | 3 | 488 | // / | | | | 489 | // |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| 490 | // +---------------+---------------+---------------+---------------+ 491 | // 0| Magic | Opcode | Key Length | 492 | // +---------------+---------------+---------------+---------------+ 493 | // 4| Extras length | Data type | Status | 494 | // +---------------+---------------+---------------+---------------+ 495 | // 8| Total body length | 496 | // +---------------+---------------+---------------+---------------+ 497 | // 12| Opaque | 498 | // +---------------+---------------+---------------+---------------+ 499 | // 16| CAS | 500 | // | | 501 | // +---------------+---------------+---------------+---------------+ 502 | // Total 24 bytes 503 | #[derive(Clone, Debug)] 504 | pub struct ResponseHeader { 505 | pub command: Command, 506 | key_len: u16, 507 | extra_len: u8, 508 | pub data_type: DataType, 509 | pub status: Status, 510 | body_len: u32, 511 | pub opaque: u32, 512 | pub cas: u64, 513 | } 514 | 515 | impl ResponseHeader { 516 | pub fn new( 517 | command: Command, 518 | data_type: DataType, 519 | status: Status, 520 | opaque: u32, 521 | cas: u64, 522 | key_len: u16, 523 | extra_len: u8, 524 | body_len: u32, 525 | ) -> ResponseHeader { 526 | ResponseHeader { 527 | command, 528 | key_len, 529 | extra_len, 530 | data_type, 531 | status, 532 | body_len, 533 | opaque, 534 | cas, 535 | } 536 | } 537 | 538 | pub fn from_payload( 539 | cmd: Command, 540 | dtype: DataType, 541 | status: Status, 542 | opaque: u32, 543 | cas: u64, 544 | key: &[u8], 545 | extra: &[u8], 546 | value: &[u8], 547 | ) -> ResponseHeader { 548 | let key_len = key.len() as u16; 549 | let extra_len = extra.len() as u8; 550 | let body_len = (key.len() + extra.len() + value.len()) as u32; 551 | 552 | ResponseHeader::new(cmd, dtype, status, opaque, cas, key_len, extra_len, body_len) 553 | } 554 | 555 | #[inline] 556 | pub fn write_to(&self, writer: &mut W) -> io::Result<()> { 557 | writer.write_u8(consts::MAGIC_RESPONSE)?; 558 | writer.write_u8(self.command.to_u8())?; 559 | writer.write_u16::(self.key_len)?; 560 | writer.write_u8(self.extra_len)?; 561 | writer.write_u8(self.data_type.to_u8())?; 562 | writer.write_u16::(self.status.to_u16())?; 563 | writer.write_u32::(self.body_len)?; 564 | writer.write_u32::(self.opaque)?; 565 | writer.write_u64::(self.cas)?; 566 | 567 | Ok(()) 568 | } 569 | 570 | #[inline] 571 | pub fn read_from(reader: &mut R) -> io::Result { 572 | let magic = reader.read_u8()?; 573 | 574 | if magic != consts::MAGIC_RESPONSE { 575 | return Err(io::Error::new(io::ErrorKind::Other, "Invalid magic")); 576 | } 577 | 578 | Ok(ResponseHeader { 579 | command: match Command::from_u8(reader.read_u8()?) { 580 | Some(c) => c, 581 | None => return Err(io::Error::new(io::ErrorKind::Other, "Invalid command")), 582 | }, 583 | key_len: reader.read_u16::()?, 584 | extra_len: reader.read_u8()?, 585 | data_type: match DataType::from_u8(reader.read_u8()?) { 586 | Some(d) => d, 587 | None => return Err(io::Error::new(io::ErrorKind::Other, "Invalid data type")), 588 | }, 589 | status: match Status::from_u16(reader.read_u16::()?) { 590 | Some(s) => s, 591 | None => return Err(io::Error::new(io::ErrorKind::Other, "Invalid status")), 592 | }, 593 | body_len: reader.read_u32::()?, 594 | opaque: reader.read_u32::()?, 595 | cas: reader.read_u64::()?, 596 | }) 597 | } 598 | } 599 | 600 | #[derive(Clone, Debug)] 601 | pub struct RequestPacket { 602 | pub header: RequestHeader, 603 | pub extra: Bytes, 604 | pub key: Bytes, 605 | pub value: Bytes, 606 | } 607 | 608 | impl RequestPacket { 609 | pub fn new( 610 | cmd: Command, 611 | dtype: DataType, 612 | vbid: u16, 613 | opaque: u32, 614 | cas: u64, 615 | extra: Bytes, 616 | key: Bytes, 617 | value: Bytes, 618 | ) -> RequestPacket { 619 | RequestPacket { 620 | header: RequestHeader::from_payload(cmd, dtype, vbid, opaque, cas, &key, &extra, &value), 621 | extra, 622 | key, 623 | value, 624 | } 625 | } 626 | 627 | #[inline] 628 | pub fn write_to(&self, writer: &mut W) -> io::Result<()> { 629 | self.header.write_to(writer)?; 630 | writer.write_all(&self.extra)?; 631 | writer.write_all(&self.key)?; 632 | writer.write_all(&self.value)?; 633 | 634 | Ok(()) 635 | } 636 | 637 | #[inline] 638 | pub fn read_from(reader: &mut R) -> io::Result { 639 | let header = RequestHeader::read_from(reader)?; 640 | 641 | let extra_len = header.extra_len as usize; 642 | let key_len = header.key_len as usize; 643 | let body_len = header.body_len as usize; 644 | 645 | let mut buf = BytesMut::with_capacity(body_len); 646 | unsafe { 647 | buf.set_len(body_len); 648 | } 649 | 650 | let mut extra = buf.split_to(extra_len); 651 | let mut key = buf.split_to(key_len); 652 | let mut value = buf; 653 | reader.read_exact(extra.as_mut())?; 654 | reader.read_exact(key.as_mut())?; 655 | reader.read_exact(value.as_mut())?; 656 | 657 | Ok(RequestPacket { 658 | header, 659 | extra: extra.freeze(), 660 | key: key.freeze(), 661 | value: value.freeze(), 662 | }) 663 | } 664 | 665 | pub fn as_ref(&self) -> RequestPacketRef<'_> { 666 | RequestPacketRef::new(&self.header, &self.extra[..], &self.key[..], &self.value[..]) 667 | } 668 | } 669 | 670 | #[derive(Debug)] 671 | pub struct RequestPacketRef<'a> { 672 | pub header: &'a RequestHeader, 673 | pub extra: &'a [u8], 674 | pub key: &'a [u8], 675 | pub value: &'a [u8], 676 | } 677 | 678 | impl<'a> RequestPacketRef<'a> { 679 | pub fn new(header: &'a RequestHeader, extra: &'a [u8], key: &'a [u8], value: &'a [u8]) -> RequestPacketRef<'a> { 680 | RequestPacketRef { 681 | header, 682 | extra, 683 | key, 684 | value, 685 | } 686 | } 687 | 688 | #[inline] 689 | pub fn write_to(&self, writer: &mut W) -> io::Result<()> { 690 | self.header.write_to(writer)?; 691 | writer.write_all(self.extra)?; 692 | writer.write_all(self.key)?; 693 | writer.write_all(self.value)?; 694 | 695 | Ok(()) 696 | } 697 | } 698 | 699 | #[derive(Clone, Debug)] 700 | pub struct ResponsePacket { 701 | pub header: ResponseHeader, 702 | pub extra: Bytes, 703 | pub key: Bytes, 704 | pub value: Bytes, 705 | } 706 | 707 | impl ResponsePacket { 708 | pub fn new( 709 | cmd: Command, 710 | dtype: DataType, 711 | status: Status, 712 | opaque: u32, 713 | cas: u64, 714 | extra: Bytes, 715 | key: Bytes, 716 | value: Bytes, 717 | ) -> ResponsePacket { 718 | ResponsePacket { 719 | header: ResponseHeader::from_payload(cmd, dtype, status, opaque, cas, &key, &extra, &value), 720 | extra, 721 | key, 722 | value, 723 | } 724 | } 725 | 726 | #[inline] 727 | pub fn write_to(&self, writer: &mut W) -> io::Result<()> { 728 | self.header.write_to(writer)?; 729 | writer.write_all(&self.extra)?; 730 | writer.write_all(&self.key)?; 731 | writer.write_all(&self.value)?; 732 | 733 | Ok(()) 734 | } 735 | 736 | #[inline] 737 | pub fn read_from(reader: &mut R) -> io::Result { 738 | let header = ResponseHeader::read_from(reader)?; 739 | 740 | let extra_len = header.extra_len as usize; 741 | let key_len = header.key_len as usize; 742 | let body_len = header.body_len as usize; 743 | 744 | let mut buf = BytesMut::with_capacity(body_len); 745 | unsafe { 746 | buf.set_len(body_len); 747 | } 748 | 749 | let mut extra = buf.split_to(extra_len); 750 | let mut key = buf.split_to(key_len); 751 | let mut value = buf; 752 | reader.read_exact(extra.as_mut())?; 753 | reader.read_exact(key.as_mut())?; 754 | reader.read_exact(value.as_mut())?; 755 | 756 | Ok(ResponsePacket { 757 | header, 758 | extra: extra.freeze(), 759 | key: key.freeze(), 760 | value: value.freeze(), 761 | }) 762 | } 763 | } 764 | 765 | pub struct ResponsePacketRef<'a> { 766 | pub header: &'a ResponseHeader, 767 | pub extra: &'a [u8], 768 | pub key: &'a [u8], 769 | pub value: &'a [u8], 770 | } 771 | 772 | impl<'a> ResponsePacketRef<'a> { 773 | pub fn new(header: &'a ResponseHeader, extra: &'a [u8], key: &'a [u8], value: &'a [u8]) -> ResponsePacketRef<'a> { 774 | ResponsePacketRef { 775 | header, 776 | extra, 777 | key, 778 | value, 779 | } 780 | } 781 | 782 | #[inline] 783 | pub fn write_to(&self, writer: &mut W) -> io::Result<()> { 784 | self.header.write_to(writer)?; 785 | writer.write_all(self.extra)?; 786 | writer.write_all(self.key)?; 787 | writer.write_all(self.value)?; 788 | 789 | Ok(()) 790 | } 791 | } 792 | 793 | #[cfg(test)] 794 | mod test { 795 | use std::io::Write; 796 | use std::net::TcpStream; 797 | 798 | use crate::proto; 799 | use crate::proto::binarydef::{Command, DataType, RequestPacket, ResponsePacket}; 800 | 801 | use bufstream::BufStream; 802 | use bytes::Bytes; 803 | 804 | fn test_stream() -> TcpStream { 805 | TcpStream::connect("127.0.0.1:11211").unwrap() 806 | } 807 | 808 | #[test] 809 | fn test_binary_protocol() { 810 | let mut stream = BufStream::new(test_stream()); 811 | 812 | { 813 | let req_packet = RequestPacket::new( 814 | Command::Set, 815 | DataType::RawBytes, 816 | 0, 817 | 0, 818 | 0, 819 | vec![0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x0e, 0x10].into(), 820 | b"test:binary_proto:hello".as_ref().into(), 821 | b"world".as_ref().into(), 822 | ); 823 | 824 | req_packet.write_to(&mut stream).unwrap(); 825 | stream.flush().unwrap(); 826 | 827 | let resp_packet = ResponsePacket::read_from(&mut stream).unwrap(); 828 | 829 | assert_eq!(resp_packet.header.status, proto::binary::Status::NoError); 830 | } 831 | 832 | { 833 | let req_packet = RequestPacket::new( 834 | Command::Get, 835 | DataType::RawBytes, 836 | 0, 837 | 0, 838 | 0, 839 | Bytes::new(), 840 | b"test:binary_proto:hello".as_ref().into(), 841 | Bytes::new(), 842 | ); 843 | 844 | req_packet.write_to(&mut stream).unwrap(); 845 | stream.flush().unwrap(); 846 | 847 | let resp_packet = ResponsePacket::read_from(&mut stream).unwrap(); 848 | 849 | assert_eq!(resp_packet.header.status, proto::binary::Status::NoError); 850 | assert_eq!(&resp_packet.value[..], b"world"); 851 | } 852 | 853 | { 854 | let req_packet = RequestPacket::new( 855 | Command::Delete, 856 | DataType::RawBytes, 857 | 0, 858 | 0, 859 | 0, 860 | Bytes::new(), 861 | b"test:binary_proto:hello".as_ref().into(), 862 | Bytes::new(), 863 | ); 864 | 865 | req_packet.write_to(&mut stream).unwrap(); 866 | stream.flush().unwrap(); 867 | 868 | let resp_packet = ResponsePacket::read_from(&mut stream).unwrap(); 869 | 870 | assert_eq!(resp_packet.header.status, proto::binary::Status::NoError); 871 | } 872 | } 873 | } 874 | -------------------------------------------------------------------------------- /src/proto/binary.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Y. T. Chung 2 | // Licensed under the Apache License, Version 2.0 3 | // or the MIT 5 | // license , 6 | // at your option. All files in the project carrying such 7 | // notice may not be copied, modified, or distributed except 8 | // according to those terms. 9 | 10 | use std::collections::{BTreeMap, HashMap}; 11 | use std::error; 12 | use std::fmt; 13 | use std::io::{BufRead, BufReader, Cursor, Write}; 14 | use std::str; 15 | use std::string::String; 16 | 17 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 18 | use bytes::Bytes; 19 | use log::debug; 20 | use semver::Version; 21 | 22 | use crate::proto::{self, AuthResponse, MemCachedResult}; 23 | use proto::binarydef::{Command, DataType, RequestHeader, RequestPacket, RequestPacketRef, ResponsePacket}; 24 | use proto::{AuthOperation, CasOperation, MultiOperation, NoReplyOperation, Operation, ServerOperation}; 25 | 26 | pub use proto::binarydef::Status; 27 | 28 | #[derive(Debug, Clone)] 29 | pub struct Error { 30 | status: Status, 31 | desc: &'static str, 32 | detail: Option, 33 | } 34 | 35 | impl Error { 36 | fn from_status(status: Status, detail: Option) -> Error { 37 | Error { 38 | status, 39 | desc: status.desc(), 40 | detail, 41 | } 42 | } 43 | 44 | /// Get error description 45 | pub fn detail(&self) -> Option { 46 | self.detail.clone() 47 | } 48 | 49 | /// Get status code 50 | pub fn status(&self) -> Status { 51 | self.status 52 | } 53 | } 54 | 55 | impl fmt::Display for Error { 56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | write!(f, "{}", self.desc)?; 58 | match self.detail { 59 | Some(ref s) => write!(f, " ({})", s), 60 | None => Ok(()), 61 | } 62 | } 63 | } 64 | 65 | impl error::Error for Error {} 66 | 67 | pub struct BinaryProto { 68 | stream: T, 69 | } 70 | 71 | // impl Proto for BinaryProto { 72 | // fn clone(&self) -> Box { 73 | // box BinaryProto { stream: BufStream::new(self.stream.get_ref().clone()) } 74 | // } 75 | // } 76 | 77 | impl BinaryProto { 78 | pub fn new(stream: T) -> BinaryProto { 79 | BinaryProto { stream } 80 | } 81 | 82 | fn send_noop(&mut self) -> MemCachedResult { 83 | let opaque = fastrand::u32(..); 84 | debug!("Sending NOOP"); 85 | let req_packet = RequestPacket::new( 86 | Command::Noop, 87 | DataType::RawBytes, 88 | 0, 89 | opaque, 90 | 0, 91 | Bytes::new(), 92 | Bytes::new(), 93 | Bytes::new(), 94 | ); 95 | 96 | req_packet.write_to(&mut self.stream)?; 97 | self.stream.flush()?; 98 | 99 | Ok(opaque) 100 | } 101 | } 102 | 103 | impl Operation for BinaryProto { 104 | fn set(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 105 | let opaque = fastrand::u32(..); 106 | debug!( 107 | "Set key: {:?} {:?}, value: {:?}, flags: 0x{:x}, expiration: {}", 108 | key, 109 | str::from_utf8(key).unwrap_or(""), 110 | value, 111 | flags, 112 | expiration 113 | ); 114 | let mut extra = [0u8; 8]; 115 | { 116 | let mut extra_buf = Cursor::new(&mut extra[..]); 117 | extra_buf.write_u32::(flags)?; 118 | extra_buf.write_u32::(expiration)?; 119 | } 120 | 121 | let req_header = 122 | RequestHeader::from_payload(Command::Set, DataType::RawBytes, 0, opaque, 0, key, &extra, value); 123 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 124 | 125 | req_packet.write_to(&mut self.stream)?; 126 | self.stream.flush()?; 127 | 128 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 129 | while resp.header.opaque != opaque { 130 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 131 | resp = ResponsePacket::read_from(&mut self.stream)?; 132 | } 133 | 134 | match resp.header.status { 135 | Status::NoError => Ok(()), 136 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 137 | } 138 | } 139 | 140 | fn add(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 141 | let opaque = fastrand::u32(..); 142 | debug!( 143 | "Add key: {:?} {:?}, value: {:?}, flags: 0x{:x}, expiration: {}", 144 | key, 145 | str::from_utf8(key).unwrap_or(""), 146 | value, 147 | flags, 148 | expiration 149 | ); 150 | let mut extra = [0u8; 8]; 151 | { 152 | let mut extra_buf = Cursor::new(&mut extra[..]); 153 | extra_buf.write_u32::(flags)?; 154 | extra_buf.write_u32::(expiration)?; 155 | } 156 | 157 | let req_header = 158 | RequestHeader::from_payload(Command::Add, DataType::RawBytes, 0, opaque, 0, key, &extra, value); 159 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 160 | 161 | req_packet.write_to(&mut self.stream)?; 162 | self.stream.flush()?; 163 | 164 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 165 | while resp.header.opaque != opaque { 166 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 167 | resp = ResponsePacket::read_from(&mut self.stream)?; 168 | } 169 | 170 | match resp.header.status { 171 | Status::NoError => Ok(()), 172 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 173 | } 174 | } 175 | 176 | fn delete(&mut self, key: &[u8]) -> MemCachedResult<()> { 177 | let opaque = fastrand::u32(..); 178 | debug!("Delete key: {:?} {:?}", key, str::from_utf8(key).unwrap_or("")); 179 | let req_header = RequestHeader::from_payload(Command::Delete, DataType::RawBytes, 0, opaque, 0, key, &[], &[]); 180 | let req_packet = RequestPacketRef::new(&req_header, &[], key, &[]); 181 | 182 | req_packet.write_to(&mut self.stream)?; 183 | self.stream.flush()?; 184 | 185 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 186 | while resp.header.opaque != opaque { 187 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 188 | resp = ResponsePacket::read_from(&mut self.stream)?; 189 | } 190 | 191 | match resp.header.status { 192 | Status::NoError => Ok(()), 193 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 194 | } 195 | } 196 | 197 | fn replace(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 198 | let opaque = fastrand::u32(..); 199 | debug!( 200 | "Replace key: {:?} {:?}, value: {:?}, flags: 0x{:x}, expiration: {}", 201 | key, 202 | str::from_utf8(key).unwrap_or(""), 203 | value, 204 | flags, 205 | expiration 206 | ); 207 | let mut extra = [0u8; 8]; 208 | { 209 | let mut extra_buf = Cursor::new(&mut extra[..]); 210 | extra_buf.write_u32::(flags)?; 211 | extra_buf.write_u32::(expiration)?; 212 | } 213 | 214 | let req_header = 215 | RequestHeader::from_payload(Command::Replace, DataType::RawBytes, 0, opaque, 0, key, &extra, value); 216 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 217 | 218 | req_packet.write_to(&mut self.stream)?; 219 | self.stream.flush()?; 220 | 221 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 222 | while resp.header.opaque != opaque { 223 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 224 | resp = ResponsePacket::read_from(&mut self.stream)?; 225 | } 226 | 227 | match resp.header.status { 228 | Status::NoError => Ok(()), 229 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 230 | } 231 | } 232 | 233 | fn get(&mut self, key: &[u8]) -> MemCachedResult<(Vec, u32)> { 234 | let opaque = fastrand::u32(..); 235 | debug!("Get key: {:?} {:?}", key, str::from_utf8(key).unwrap_or("")); 236 | let req_header = RequestHeader::from_payload(Command::Get, DataType::RawBytes, 0, opaque, 0, key, &[], &[]); 237 | let req_packet = RequestPacketRef::new(&req_header, &[], key, &[]); 238 | 239 | req_packet.write_to(&mut self.stream)?; 240 | self.stream.flush()?; 241 | 242 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 243 | while resp.header.opaque != opaque { 244 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 245 | resp = ResponsePacket::read_from(&mut self.stream)?; 246 | } 247 | 248 | match resp.header.status { 249 | Status::NoError => { 250 | let mut extrabufr = BufReader::new(&resp.extra[..]); 251 | let flags = extrabufr.read_u32::()?; 252 | 253 | Ok((resp.value.to_vec(), flags)) 254 | } 255 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 256 | } 257 | } 258 | 259 | fn getk(&mut self, key: &[u8]) -> MemCachedResult<(Vec, Vec, u32)> { 260 | let opaque = fastrand::u32(..); 261 | debug!("GetK key: {:?} {:?}", key, str::from_utf8(key).unwrap_or("")); 262 | let req_header = RequestHeader::from_payload(Command::GetKey, DataType::RawBytes, 0, opaque, 0, key, &[], &[]); 263 | let req_packet = RequestPacketRef::new(&req_header, &[], key, &[]); 264 | 265 | req_packet.write_to(&mut self.stream)?; 266 | self.stream.flush()?; 267 | 268 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 269 | while resp.header.opaque != opaque { 270 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 271 | resp = ResponsePacket::read_from(&mut self.stream)?; 272 | } 273 | 274 | match resp.header.status { 275 | Status::NoError => { 276 | let mut extrabufr = BufReader::new(&resp.extra[..]); 277 | let flags = extrabufr.read_u32::()?; 278 | 279 | Ok((resp.key.to_vec(), resp.value.to_vec(), flags)) 280 | } 281 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 282 | } 283 | } 284 | 285 | fn increment(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult { 286 | let opaque = fastrand::u32(..); 287 | debug!( 288 | "Increment key: {:?} {:?}, amount: {}, initial: {}, expiration: {}", 289 | key, 290 | str::from_utf8(key).unwrap_or(""), 291 | amount, 292 | initial, 293 | expiration 294 | ); 295 | let mut extra = [0u8; 20]; 296 | { 297 | let mut extra_buf = Cursor::new(&mut extra[..]); 298 | extra_buf.write_u64::(amount)?; 299 | extra_buf.write_u64::(initial)?; 300 | extra_buf.write_u32::(expiration)?; 301 | } 302 | 303 | let req_header = 304 | RequestHeader::from_payload(Command::Increment, DataType::RawBytes, 0, opaque, 0, key, &extra, &[]); 305 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, &[]); 306 | 307 | req_packet.write_to(&mut self.stream)?; 308 | self.stream.flush()?; 309 | 310 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 311 | while resp.header.opaque != opaque { 312 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 313 | resp = ResponsePacket::read_from(&mut self.stream)?; 314 | } 315 | 316 | match resp.header.status { 317 | Status::NoError => { 318 | let mut bufr = BufReader::new(&resp.value[..]); 319 | Ok(bufr.read_u64::()?) 320 | } 321 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 322 | } 323 | } 324 | 325 | fn decrement(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult { 326 | let opaque = fastrand::u32(..); 327 | debug!( 328 | "Decrement key: {:?} {:?}, amount: {}, initial: {}, expiration: {}", 329 | key, 330 | str::from_utf8(key).unwrap_or(""), 331 | amount, 332 | initial, 333 | expiration 334 | ); 335 | let mut extra = [0u8; 20]; 336 | { 337 | let mut extra_buf = Cursor::new(&mut extra[..]); 338 | extra_buf.write_u64::(amount)?; 339 | extra_buf.write_u64::(initial)?; 340 | extra_buf.write_u32::(expiration)?; 341 | } 342 | 343 | let req_header = 344 | RequestHeader::from_payload(Command::Decrement, DataType::RawBytes, 0, opaque, 0, key, &extra, &[]); 345 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, &[]); 346 | 347 | req_packet.write_to(&mut self.stream)?; 348 | self.stream.flush()?; 349 | 350 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 351 | while resp.header.opaque != opaque { 352 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 353 | resp = ResponsePacket::read_from(&mut self.stream)?; 354 | } 355 | 356 | match resp.header.status { 357 | Status::NoError => { 358 | let mut bufr = BufReader::new(&resp.value[..]); 359 | Ok(bufr.read_u64::()?) 360 | } 361 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 362 | } 363 | } 364 | 365 | fn append(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()> { 366 | let opaque = fastrand::u32(..); 367 | debug!("Append key: {:?} {:?}, value: {:?}", key, str::from_utf8(key).unwrap_or(""), value); 368 | let req_header = 369 | RequestHeader::from_payload(Command::Append, DataType::RawBytes, 0, opaque, 0, key, &[], value); 370 | let req_packet = RequestPacketRef::new(&req_header, &[], key, value); 371 | 372 | req_packet.write_to(&mut self.stream)?; 373 | self.stream.flush()?; 374 | 375 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 376 | while resp.header.opaque != opaque { 377 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 378 | resp = ResponsePacket::read_from(&mut self.stream)?; 379 | } 380 | 381 | match resp.header.status { 382 | Status::NoError => Ok(()), 383 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 384 | } 385 | } 386 | 387 | fn prepend(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()> { 388 | let opaque = fastrand::u32(..); 389 | debug!("Prepend key: {:?} {:?}, value: {:?}", key, str::from_utf8(key).unwrap_or(""), value); 390 | let req_header = 391 | RequestHeader::from_payload(Command::Prepend, DataType::RawBytes, 0, opaque, 0, key, &[], value); 392 | let req_packet = RequestPacketRef::new(&req_header, &[], key, value); 393 | 394 | req_packet.write_to(&mut self.stream)?; 395 | self.stream.flush()?; 396 | 397 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 398 | while resp.header.opaque != opaque { 399 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 400 | resp = ResponsePacket::read_from(&mut self.stream)?; 401 | } 402 | 403 | match resp.header.status { 404 | Status::NoError => Ok(()), 405 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 406 | } 407 | } 408 | 409 | fn touch(&mut self, key: &[u8], expiration: u32) -> MemCachedResult<()> { 410 | let opaque = fastrand::u32(..); 411 | debug!( 412 | "Touch key: {:?} {:?}, expiration: {}", 413 | key, 414 | str::from_utf8(key).unwrap_or(""), 415 | expiration 416 | ); 417 | let mut extra = [0u8; 4]; 418 | { 419 | let mut extra_buf = Cursor::new(&mut extra[..]); 420 | extra_buf.write_u32::(expiration)?; 421 | } 422 | 423 | let req_header = 424 | RequestHeader::from_payload(Command::Touch, DataType::RawBytes, 0, opaque, 0, key, &extra, &[]); 425 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, &[]); 426 | 427 | req_packet.write_to(&mut self.stream)?; 428 | self.stream.flush()?; 429 | 430 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 431 | while resp.header.opaque != opaque { 432 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 433 | resp = ResponsePacket::read_from(&mut self.stream)?; 434 | } 435 | 436 | match resp.header.status { 437 | Status::NoError => Ok(()), 438 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 439 | } 440 | } 441 | } 442 | 443 | impl ServerOperation for BinaryProto { 444 | fn quit(&mut self) -> MemCachedResult<()> { 445 | let opaque = fastrand::u32(..); 446 | debug!("Quit"); 447 | let req_header = RequestHeader::from_payload(Command::Quit, DataType::RawBytes, 0, opaque, 0, &[], &[], &[]); 448 | let req_packet = RequestPacketRef::new(&req_header, &[], &[], &[]); 449 | 450 | req_packet.write_to(&mut self.stream)?; 451 | self.stream.flush()?; 452 | 453 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 454 | while resp.header.opaque != opaque { 455 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 456 | resp = ResponsePacket::read_from(&mut self.stream)?; 457 | } 458 | 459 | match resp.header.status { 460 | Status::NoError => Ok(()), 461 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 462 | } 463 | } 464 | 465 | fn flush(&mut self, expiration: u32) -> MemCachedResult<()> { 466 | let opaque = fastrand::u32(..); 467 | debug!("Expiration flush: {}", expiration); 468 | let mut extra = [0u8; 4]; 469 | { 470 | let mut extra_buf = Cursor::new(&mut extra[..]); 471 | extra_buf.write_u32::(expiration)?; 472 | } 473 | 474 | let req_header = 475 | RequestHeader::from_payload(Command::Flush, DataType::RawBytes, 0, opaque, 0, &[], &extra, &[]); 476 | let req_packet = RequestPacketRef::new(&req_header, &extra, &[], &[]); 477 | 478 | req_packet.write_to(&mut self.stream)?; 479 | self.stream.flush()?; 480 | 481 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 482 | while resp.header.opaque != opaque { 483 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 484 | resp = ResponsePacket::read_from(&mut self.stream)?; 485 | } 486 | 487 | match resp.header.status { 488 | Status::NoError => Ok(()), 489 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 490 | } 491 | } 492 | 493 | fn noop(&mut self) -> MemCachedResult<()> { 494 | debug!("Noop"); 495 | let opaque = self.send_noop()?; 496 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 497 | while resp.header.opaque != opaque { 498 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 499 | resp = ResponsePacket::read_from(&mut self.stream)?; 500 | } 501 | 502 | match resp.header.status { 503 | Status::NoError => Ok(()), 504 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 505 | } 506 | } 507 | 508 | fn version(&mut self) -> MemCachedResult { 509 | let opaque = fastrand::u32(..); 510 | debug!("Version"); 511 | let req_header = RequestHeader::new(Command::Version, DataType::RawBytes, 0, opaque, 0, 0, 0, 0); 512 | let req_packet = RequestPacketRef::new(&req_header, &[], &[], &[]); 513 | 514 | req_packet.write_to(&mut self.stream)?; 515 | self.stream.flush()?; 516 | 517 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 518 | while resp.header.opaque != opaque { 519 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 520 | resp = ResponsePacket::read_from(&mut self.stream)?; 521 | } 522 | 523 | match resp.header.status { 524 | Status::NoError => { 525 | let val = resp.value; 526 | let verstr = match str::from_utf8(&val[..]) { 527 | Ok(vs) => vs, 528 | Err(..) => { 529 | return Err(proto::Error::OtherError { 530 | desc: "Response is not a string", 531 | detail: None, 532 | }) 533 | } 534 | }; 535 | 536 | Ok(match Version::parse(verstr) { 537 | Ok(v) => v, 538 | Err(err) => { 539 | return Err(proto::Error::OtherError { 540 | desc: "Unrecognized version string", 541 | detail: Some(err.to_string()), 542 | }) 543 | } 544 | }) 545 | } 546 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 547 | } 548 | } 549 | 550 | fn stat(&mut self) -> MemCachedResult> { 551 | let opaque = fastrand::u32(..); 552 | debug!("Stat"); 553 | let req_header = RequestHeader::new(Command::Stat, DataType::RawBytes, 0, opaque, 0, 0, 0, 0); 554 | let req_packet = RequestPacketRef::new(&req_header, &[], &[], &[]); 555 | 556 | req_packet.write_to(&mut self.stream)?; 557 | self.stream.flush()?; 558 | 559 | let mut result = BTreeMap::new(); 560 | loop { 561 | let resp = ResponsePacket::read_from(&mut self.stream)?; 562 | if resp.header.opaque != opaque { 563 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 564 | continue; 565 | } 566 | match resp.header.status { 567 | Status::NoError => {} 568 | _ => return Err(From::from(Error::from_status(resp.header.status, None))), 569 | } 570 | 571 | if resp.key.is_empty() && resp.value.is_empty() { 572 | break; 573 | } 574 | 575 | let key = match String::from_utf8(resp.key.to_vec()) { 576 | Ok(k) => k, 577 | Err(..) => { 578 | return Err(proto::Error::OtherError { 579 | desc: "Key is not a string", 580 | detail: None, 581 | }) 582 | } 583 | }; 584 | 585 | let val = match String::from_utf8(resp.value.to_vec()) { 586 | Ok(k) => k, 587 | Err(..) => { 588 | return Err(proto::Error::OtherError { 589 | desc: "Value is not a string", 590 | detail: None, 591 | }) 592 | } 593 | }; 594 | 595 | result.insert(key, val); 596 | } 597 | 598 | Ok(result) 599 | } 600 | } 601 | 602 | impl MultiOperation for BinaryProto { 603 | fn set_multi(&mut self, kv: BTreeMap<&[u8], (&[u8], u32, u32)>) -> MemCachedResult<()> { 604 | for (key, (value, flags, expiration)) in kv.into_iter() { 605 | let mut extra = [0u8; 8]; 606 | { 607 | let mut extra_buf = Cursor::new(&mut extra[..]); 608 | extra_buf.write_u32::(flags)?; 609 | extra_buf.write_u32::(expiration)?; 610 | } 611 | 612 | let req_header = 613 | RequestHeader::from_payload(Command::SetQuietly, DataType::RawBytes, 0, 0, 0, key, &extra, value); 614 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 615 | 616 | req_packet.write_to(&mut self.stream)?; 617 | } 618 | self.send_noop()?; 619 | 620 | loop { 621 | let resp = ResponsePacket::read_from(&mut self.stream)?; 622 | 623 | match resp.header.status { 624 | Status::NoError => {} 625 | _ => return Err(From::from(Error::from_status(resp.header.status, None))), 626 | } 627 | 628 | if resp.header.command == Command::Noop { 629 | return Ok(()); 630 | } 631 | } 632 | } 633 | 634 | fn delete_multi(&mut self, keys: &[&[u8]]) -> MemCachedResult<()> { 635 | for key in keys.iter() { 636 | let req_header = 637 | RequestHeader::from_payload(Command::DeleteQuietly, DataType::RawBytes, 0, 0, 0, *key, &[], &[]); 638 | let req_packet = RequestPacketRef::new(&req_header, &[], *key, &[]); 639 | 640 | req_packet.write_to(&mut self.stream)?; 641 | } 642 | self.send_noop()?; 643 | 644 | loop { 645 | let resp = ResponsePacket::read_from(&mut self.stream)?; 646 | 647 | match resp.header.status { 648 | Status::NoError | Status::KeyNotFound => {} 649 | _ => return Err(From::from(Error::from_status(resp.header.status, None))), 650 | } 651 | 652 | if resp.header.command == Command::Noop { 653 | return Ok(()); 654 | } 655 | } 656 | } 657 | 658 | fn increment_multi<'a>( 659 | &mut self, 660 | kv: HashMap<&'a [u8], (u64, u64, u32)>, 661 | ) -> MemCachedResult> { 662 | let opaques: MemCachedResult> = kv 663 | .into_iter() 664 | .map(|(key, (amount, initial, expiration))| { 665 | let opaque = fastrand::u32(..); 666 | let mut extra = [0u8; 20]; 667 | { 668 | let mut extra_buf = Cursor::new(&mut extra[..]); 669 | extra_buf.write_u64::(amount)?; 670 | extra_buf.write_u64::(initial)?; 671 | extra_buf.write_u32::(expiration)?; 672 | } 673 | 674 | let req_header = 675 | RequestHeader::from_payload(Command::Increment, DataType::RawBytes, 0, opaque, 0, key, &extra, &[]); 676 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, &[]); 677 | 678 | req_packet.write_to(&mut self.stream)?; 679 | Ok((opaque, key)) 680 | }) 681 | .collect(); 682 | 683 | let opaques = opaques?; 684 | 685 | self.send_noop()?; 686 | self.stream.flush()?; 687 | 688 | let mut results = HashMap::with_capacity(opaques.len()); 689 | loop { 690 | let resp = ResponsePacket::read_from(&mut self.stream)?; 691 | match resp.header.status { 692 | Status::NoError => {} 693 | _ => return Err(From::from(Error::from_status(resp.header.status, None))), 694 | } 695 | 696 | if resp.header.command == Command::Noop { 697 | return Ok(results); 698 | } 699 | 700 | if let Some(key) = opaques.get(&resp.header.opaque) { 701 | let mut bufr = BufReader::new(&resp.value[..]); 702 | let val = bufr.read_u64::()?; 703 | results.insert(key, val); 704 | } 705 | } 706 | } 707 | 708 | fn get_multi(&mut self, keys: &[&[u8]]) -> MemCachedResult, (Vec, u32)>> { 709 | for key in keys.iter() { 710 | let req_header = 711 | RequestHeader::from_payload(Command::GetKeyQuietly, DataType::RawBytes, 0, 0, 0, *key, &[], &[]); 712 | let req_packet = RequestPacketRef::new(&req_header, &[], *key, &[]); 713 | 714 | req_packet.write_to(&mut self.stream)?; 715 | } 716 | self.send_noop()?; 717 | 718 | let mut result = HashMap::with_capacity(keys.len()); 719 | loop { 720 | let resp = ResponsePacket::read_from(&mut self.stream)?; 721 | match resp.header.status { 722 | Status::NoError => {} 723 | _ => return Err(From::from(Error::from_status(resp.header.status, None))), 724 | } 725 | 726 | if resp.header.command == Command::Noop { 727 | return Ok(result); 728 | } 729 | 730 | let mut extrabufr = BufReader::new(&resp.extra[..]); 731 | let flags = extrabufr.read_u32::()?; 732 | 733 | result.insert(resp.key.to_vec(), (resp.value.to_vec(), flags)); 734 | } 735 | } 736 | } 737 | 738 | impl NoReplyOperation for BinaryProto { 739 | fn set_noreply(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 740 | let opaque = fastrand::u32(..); 741 | debug!( 742 | "Set noreply key: {:?} {:?}, value: {:?}, flags: 0x{:x}, expiration: {}", 743 | key, 744 | str::from_utf8(key).unwrap_or(""), 745 | value, 746 | flags, 747 | expiration 748 | ); 749 | let mut extra = [0u8; 8]; 750 | { 751 | let mut extra_buf = Cursor::new(&mut extra[..]); 752 | extra_buf.write_u32::(flags)?; 753 | extra_buf.write_u32::(expiration)?; 754 | } 755 | 756 | let req_header = 757 | RequestHeader::from_payload(Command::SetQuietly, DataType::RawBytes, 0, opaque, 0, key, &extra, value); 758 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 759 | 760 | req_packet.write_to(&mut self.stream)?; 761 | self.stream.flush()?; 762 | 763 | Ok(()) 764 | } 765 | 766 | fn add_noreply(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 767 | let opaque = fastrand::u32(..); 768 | debug!( 769 | "Add noreply key: {:?} {:?}, value: {:?}, flags: 0x{:x}, expiration: {}", 770 | key, 771 | str::from_utf8(key).unwrap_or(""), 772 | value, 773 | flags, 774 | expiration 775 | ); 776 | let mut extra = [0u8; 8]; 777 | { 778 | let mut extra_buf = Cursor::new(&mut extra[..]); 779 | extra_buf.write_u32::(flags)?; 780 | extra_buf.write_u32::(expiration)?; 781 | } 782 | 783 | let req_header = 784 | RequestHeader::from_payload(Command::AddQuietly, DataType::RawBytes, 0, opaque, 0, key, &extra, value); 785 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 786 | 787 | req_packet.write_to(&mut self.stream)?; 788 | self.stream.flush()?; 789 | 790 | Ok(()) 791 | } 792 | 793 | fn delete_noreply(&mut self, key: &[u8]) -> MemCachedResult<()> { 794 | let opaque = fastrand::u32(..); 795 | debug!("Delete noreply key: {:?} {:?}", key, str::from_utf8(key).unwrap_or("")); 796 | let req_header = 797 | RequestHeader::from_payload(Command::DeleteQuietly, DataType::RawBytes, 0, opaque, 0, key, &[], &[]); 798 | let req_packet = RequestPacketRef::new(&req_header, &[], key, &[]); 799 | 800 | req_packet.write_to(&mut self.stream)?; 801 | self.stream.flush()?; 802 | 803 | Ok(()) 804 | } 805 | 806 | fn replace_noreply(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult<()> { 807 | let opaque = fastrand::u32(..); 808 | debug!( 809 | "Replace noreply key: {:?} {:?}, value: {:?}, flags: 0x{:x}, expiration: {}", 810 | key, 811 | str::from_utf8(key).unwrap_or(""), 812 | value, 813 | flags, 814 | expiration 815 | ); 816 | let mut extra = [0u8; 8]; 817 | { 818 | let mut extra_buf = Cursor::new(&mut extra[..]); 819 | extra_buf.write_u32::(flags)?; 820 | extra_buf.write_u32::(expiration)?; 821 | } 822 | 823 | let req_header = 824 | RequestHeader::from_payload(Command::ReplaceQuietly, DataType::RawBytes, 0, opaque, 0, key, &extra, value); 825 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 826 | 827 | req_packet.write_to(&mut self.stream)?; 828 | self.stream.flush()?; 829 | 830 | Ok(()) 831 | } 832 | 833 | fn increment_noreply(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult<()> { 834 | let opaque = fastrand::u32(..); 835 | debug!( 836 | "Increment noreply key: {:?} {:?}, amount: {}, initial: {}, expiration: {}", 837 | key, 838 | str::from_utf8(key).unwrap_or(""), 839 | amount, 840 | initial, 841 | expiration 842 | ); 843 | let mut extra = [0u8; 20]; 844 | { 845 | let mut extra_buf = Cursor::new(&mut extra[..]); 846 | extra_buf.write_u64::(amount)?; 847 | extra_buf.write_u64::(initial)?; 848 | extra_buf.write_u32::(expiration)?; 849 | } 850 | 851 | let req_header = 852 | RequestHeader::from_payload(Command::IncrementQuietly, DataType::RawBytes, 0, opaque, 0, key, &extra, &[]); 853 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, &[]); 854 | 855 | req_packet.write_to(&mut self.stream)?; 856 | self.stream.flush()?; 857 | 858 | Ok(()) 859 | } 860 | 861 | fn decrement_noreply(&mut self, key: &[u8], amount: u64, initial: u64, expiration: u32) -> MemCachedResult<()> { 862 | let opaque = fastrand::u32(..); 863 | debug!( 864 | "Decrement noreply key: {:?} {:?}, amount: {}, initial: {}, expiration: {}", 865 | key, 866 | str::from_utf8(key).unwrap_or(""), 867 | amount, 868 | initial, 869 | expiration 870 | ); 871 | let mut extra = [0u8; 20]; 872 | { 873 | let mut extra_buf = Cursor::new(&mut extra[..]); 874 | extra_buf.write_u64::(amount)?; 875 | extra_buf.write_u64::(initial)?; 876 | extra_buf.write_u32::(expiration)?; 877 | } 878 | 879 | let req_header = 880 | RequestHeader::from_payload(Command::DecrementQuietly, DataType::RawBytes, 0, opaque, 0, key, &extra, &[]); 881 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, &[]); 882 | 883 | req_packet.write_to(&mut self.stream)?; 884 | self.stream.flush()?; 885 | 886 | Ok(()) 887 | } 888 | 889 | fn append_noreply(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()> { 890 | let opaque = fastrand::u32(..); 891 | debug!( 892 | "Append noreply key: {:?} {:?}, value: {:?}", 893 | key, 894 | str::from_utf8(key).unwrap_or(""), 895 | value 896 | ); 897 | let req_header = 898 | RequestHeader::from_payload(Command::AppendQuietly, DataType::RawBytes, 0, opaque, 0, key, &[], value); 899 | let req_packet = RequestPacketRef::new(&req_header, &[], key, value); 900 | 901 | req_packet.write_to(&mut self.stream)?; 902 | self.stream.flush()?; 903 | 904 | Ok(()) 905 | } 906 | 907 | fn prepend_noreply(&mut self, key: &[u8], value: &[u8]) -> MemCachedResult<()> { 908 | let opaque = fastrand::u32(..); 909 | debug!( 910 | "Prepend noreply key: {:?} {:?}, value: {:?}", 911 | key, 912 | str::from_utf8(key).unwrap_or(""), 913 | value 914 | ); 915 | let req_header = 916 | RequestHeader::from_payload(Command::PrependQuietly, DataType::RawBytes, 0, opaque, 0, key, &[], value); 917 | let req_packet = RequestPacketRef::new(&req_header, &[], key, value); 918 | 919 | req_packet.write_to(&mut self.stream)?; 920 | self.stream.flush()?; 921 | 922 | Ok(()) 923 | } 924 | } 925 | 926 | impl CasOperation for BinaryProto { 927 | fn set_cas(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32, cas: u64) -> MemCachedResult { 928 | let opaque = fastrand::u32(..); 929 | debug!( 930 | "Set cas key: {:?} {:?}, value: {:?}, flags: 0x{:x}, expiration: {}, cas: {}", 931 | key, 932 | str::from_utf8(key).unwrap_or(""), 933 | value, 934 | flags, 935 | expiration, 936 | cas 937 | ); 938 | let mut extra = [0u8; 8]; 939 | { 940 | let mut extra_buf = Cursor::new(&mut extra[..]); 941 | extra_buf.write_u32::(flags)?; 942 | extra_buf.write_u32::(expiration)?; 943 | } 944 | 945 | let req_header = 946 | RequestHeader::from_payload(Command::Set, DataType::RawBytes, 0, opaque, cas, key, &extra, value); 947 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 948 | 949 | req_packet.write_to(&mut self.stream)?; 950 | self.stream.flush()?; 951 | 952 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 953 | while resp.header.opaque != opaque { 954 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 955 | resp = ResponsePacket::read_from(&mut self.stream)?; 956 | } 957 | 958 | match resp.header.status { 959 | Status::NoError => Ok(resp.header.cas), 960 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 961 | } 962 | } 963 | 964 | fn add_cas(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32) -> MemCachedResult { 965 | let opaque = fastrand::u32(..); 966 | debug!( 967 | "Add cas key: {:?} {:?}, value: {:?}, flags: 0x{:x}, expiration: {}", 968 | key, 969 | str::from_utf8(key).unwrap_or(""), 970 | value, 971 | flags, 972 | expiration 973 | ); 974 | let mut extra = [0u8; 8]; 975 | { 976 | let mut extra_buf = Cursor::new(&mut extra[..]); 977 | extra_buf.write_u32::(flags)?; 978 | extra_buf.write_u32::(expiration)?; 979 | } 980 | 981 | let req_header = 982 | RequestHeader::from_payload(Command::Add, DataType::RawBytes, 0, opaque, 0, key, &extra, value); 983 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 984 | 985 | req_packet.write_to(&mut self.stream)?; 986 | self.stream.flush()?; 987 | 988 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 989 | while resp.header.opaque != opaque { 990 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 991 | resp = ResponsePacket::read_from(&mut self.stream)?; 992 | } 993 | 994 | match resp.header.status { 995 | Status::NoError => Ok(resp.header.cas), 996 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 997 | } 998 | } 999 | 1000 | fn replace_cas(&mut self, key: &[u8], value: &[u8], flags: u32, expiration: u32, cas: u64) -> MemCachedResult { 1001 | let opaque = fastrand::u32(..); 1002 | debug!( 1003 | "Replace cas key: {:?} {:?}, value: {:?}, flags: 0x{:x}, expiration: {}, cas: {}", 1004 | key, 1005 | str::from_utf8(key).unwrap_or(""), 1006 | value, 1007 | flags, 1008 | expiration, 1009 | cas 1010 | ); 1011 | let mut extra = [0u8; 8]; 1012 | { 1013 | let mut extra_buf = Cursor::new(&mut extra[..]); 1014 | extra_buf.write_u32::(flags)?; 1015 | extra_buf.write_u32::(expiration)?; 1016 | } 1017 | 1018 | let req_header = 1019 | RequestHeader::from_payload(Command::Replace, DataType::RawBytes, 0, opaque, cas, key, &extra, value); 1020 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, value); 1021 | 1022 | req_packet.write_to(&mut self.stream)?; 1023 | self.stream.flush()?; 1024 | 1025 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1026 | while resp.header.opaque != opaque { 1027 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1028 | resp = ResponsePacket::read_from(&mut self.stream)?; 1029 | } 1030 | 1031 | match resp.header.status { 1032 | Status::NoError => Ok(resp.header.cas), 1033 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1034 | } 1035 | } 1036 | 1037 | fn get_cas(&mut self, key: &[u8]) -> MemCachedResult<(Vec, u32, u64)> { 1038 | let opaque = fastrand::u32(..); 1039 | debug!("Get cas key: {:?} {:?}", key, str::from_utf8(key).unwrap_or("")); 1040 | let req_header = RequestHeader::from_payload(Command::Get, DataType::RawBytes, 0, opaque, 0, key, &[], &[]); 1041 | let req_packet = RequestPacketRef::new(&req_header, &[], key, &[]); 1042 | 1043 | req_packet.write_to(&mut self.stream)?; 1044 | self.stream.flush()?; 1045 | 1046 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1047 | while resp.header.opaque != opaque { 1048 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1049 | resp = ResponsePacket::read_from(&mut self.stream)?; 1050 | } 1051 | 1052 | match resp.header.status { 1053 | Status::NoError => { 1054 | let mut extrabufr = BufReader::new(&resp.extra[..]); 1055 | let flags = extrabufr.read_u32::()?; 1056 | 1057 | Ok((resp.value.to_vec(), flags, resp.header.cas)) 1058 | } 1059 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1060 | } 1061 | } 1062 | 1063 | fn getk_cas(&mut self, key: &[u8]) -> MemCachedResult<(Vec, Vec, u32, u64)> { 1064 | let opaque = fastrand::u32(..); 1065 | debug!("GetK cas key: {:?} {:?}", key, str::from_utf8(key).unwrap_or("")); 1066 | let req_header = RequestHeader::from_payload(Command::GetKey, DataType::RawBytes, 0, opaque, 0, key, &[], &[]); 1067 | let req_packet = RequestPacketRef::new(&req_header, &[], key, &[]); 1068 | 1069 | req_packet.write_to(&mut self.stream)?; 1070 | self.stream.flush()?; 1071 | 1072 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1073 | while resp.header.opaque != opaque { 1074 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1075 | resp = ResponsePacket::read_from(&mut self.stream)?; 1076 | } 1077 | 1078 | match resp.header.status { 1079 | Status::NoError => { 1080 | let mut extrabufr = BufReader::new(&resp.extra[..]); 1081 | let flags = extrabufr.read_u32::()?; 1082 | 1083 | Ok((resp.key.to_vec(), resp.value.to_vec(), flags, resp.header.cas)) 1084 | } 1085 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1086 | } 1087 | } 1088 | 1089 | fn increment_cas( 1090 | &mut self, 1091 | key: &[u8], 1092 | amount: u64, 1093 | initial: u64, 1094 | expiration: u32, 1095 | cas: u64, 1096 | ) -> MemCachedResult<(u64, u64)> { 1097 | let opaque = fastrand::u32(..); 1098 | debug!( 1099 | "Increment cas key: {:?} {:?}, amount: {}, initial: {}, expiration: {}, cas: {}", 1100 | key, 1101 | str::from_utf8(key).unwrap_or(""), 1102 | amount, 1103 | initial, 1104 | expiration, 1105 | cas 1106 | ); 1107 | let mut extra = [0u8; 20]; 1108 | { 1109 | let mut extra_buf = Cursor::new(&mut extra[..]); 1110 | extra_buf.write_u64::(amount)?; 1111 | extra_buf.write_u64::(initial)?; 1112 | extra_buf.write_u32::(expiration)?; 1113 | } 1114 | 1115 | let req_header = 1116 | RequestHeader::from_payload(Command::Increment, DataType::RawBytes, 0, opaque, cas, key, &extra, &[]); 1117 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, &[]); 1118 | 1119 | req_packet.write_to(&mut self.stream)?; 1120 | self.stream.flush()?; 1121 | 1122 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1123 | while resp.header.opaque != opaque { 1124 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1125 | resp = ResponsePacket::read_from(&mut self.stream)?; 1126 | } 1127 | 1128 | match resp.header.status { 1129 | Status::NoError => { 1130 | let mut bufr = BufReader::new(&resp.value[..]); 1131 | Ok((bufr.read_u64::()?, resp.header.cas)) 1132 | } 1133 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1134 | } 1135 | } 1136 | 1137 | fn decrement_cas( 1138 | &mut self, 1139 | key: &[u8], 1140 | amount: u64, 1141 | initial: u64, 1142 | expiration: u32, 1143 | cas: u64, 1144 | ) -> MemCachedResult<(u64, u64)> { 1145 | let opaque = fastrand::u32(..); 1146 | debug!( 1147 | "Decrement cas key: {:?} {:?}, amount: {}, initial: {}, expiration: {}, cas: {}", 1148 | key, 1149 | str::from_utf8(key).unwrap_or(""), 1150 | amount, 1151 | initial, 1152 | expiration, 1153 | cas 1154 | ); 1155 | let mut extra = [0u8; 20]; 1156 | { 1157 | let mut extra_buf = Cursor::new(&mut extra[..]); 1158 | extra_buf.write_u64::(amount)?; 1159 | extra_buf.write_u64::(initial)?; 1160 | extra_buf.write_u32::(expiration)?; 1161 | } 1162 | 1163 | let req_header = 1164 | RequestHeader::from_payload(Command::Decrement, DataType::RawBytes, 0, opaque, cas, key, &extra, &[]); 1165 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, &[]); 1166 | 1167 | req_packet.write_to(&mut self.stream)?; 1168 | self.stream.flush()?; 1169 | 1170 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1171 | while resp.header.opaque != opaque { 1172 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1173 | resp = ResponsePacket::read_from(&mut self.stream)?; 1174 | } 1175 | 1176 | match resp.header.status { 1177 | Status::NoError => { 1178 | let mut bufr = BufReader::new(&resp.value[..]); 1179 | Ok((bufr.read_u64::()?, resp.header.cas)) 1180 | } 1181 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1182 | } 1183 | } 1184 | 1185 | fn append_cas(&mut self, key: &[u8], value: &[u8], cas: u64) -> MemCachedResult { 1186 | let opaque = fastrand::u32(..); 1187 | debug!( 1188 | "Append cas key: {:?} {:?}, value: {:?}, cas: {}", 1189 | key, 1190 | str::from_utf8(key).unwrap_or(""), 1191 | value, 1192 | cas 1193 | ); 1194 | let req_header = 1195 | RequestHeader::from_payload(Command::Append, DataType::RawBytes, 0, opaque, cas, key, &[], value); 1196 | let req_packet = RequestPacketRef::new(&req_header, &[], key, value); 1197 | 1198 | req_packet.write_to(&mut self.stream)?; 1199 | self.stream.flush()?; 1200 | 1201 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1202 | while resp.header.opaque != opaque { 1203 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1204 | resp = ResponsePacket::read_from(&mut self.stream)?; 1205 | } 1206 | 1207 | match resp.header.status { 1208 | Status::NoError => Ok(resp.header.cas), 1209 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1210 | } 1211 | } 1212 | 1213 | fn prepend_cas(&mut self, key: &[u8], value: &[u8], cas: u64) -> MemCachedResult { 1214 | let opaque = fastrand::u32(..); 1215 | debug!( 1216 | "Prepend cas key: {:?} {:?}, value: {:?}, cas: {}", 1217 | key, 1218 | str::from_utf8(key).unwrap_or(""), 1219 | value, 1220 | cas 1221 | ); 1222 | let req_header = 1223 | RequestHeader::from_payload(Command::Prepend, DataType::RawBytes, 0, opaque, cas, key, &[], value); 1224 | let req_packet = RequestPacketRef::new(&req_header, &[], key, value); 1225 | 1226 | req_packet.write_to(&mut self.stream)?; 1227 | self.stream.flush()?; 1228 | 1229 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1230 | while resp.header.opaque != opaque { 1231 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1232 | resp = ResponsePacket::read_from(&mut self.stream)?; 1233 | } 1234 | 1235 | match resp.header.status { 1236 | Status::NoError => Ok(resp.header.cas), 1237 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1238 | } 1239 | } 1240 | 1241 | fn touch_cas(&mut self, key: &[u8], expiration: u32, cas: u64) -> MemCachedResult { 1242 | let opaque = fastrand::u32(..); 1243 | debug!( 1244 | "Touch cas key: {:?} {:?}, expiration: {:?}, cas: {}", 1245 | key, 1246 | str::from_utf8(key).unwrap_or(""), 1247 | expiration, 1248 | cas 1249 | ); 1250 | let mut extra = [0u8; 4]; 1251 | { 1252 | let mut extra_buf = Cursor::new(&mut extra[..]); 1253 | extra_buf.write_u32::(expiration)?; 1254 | } 1255 | 1256 | let req_header = 1257 | RequestHeader::from_payload(Command::Touch, DataType::RawBytes, 0, opaque, cas, key, &extra, &[]); 1258 | let req_packet = RequestPacketRef::new(&req_header, &extra, key, &[]); 1259 | 1260 | req_packet.write_to(&mut self.stream)?; 1261 | self.stream.flush()?; 1262 | 1263 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1264 | while resp.header.opaque != opaque { 1265 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1266 | resp = ResponsePacket::read_from(&mut self.stream)?; 1267 | } 1268 | 1269 | match resp.header.status { 1270 | Status::NoError => Ok(resp.header.cas), 1271 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1272 | } 1273 | } 1274 | } 1275 | 1276 | impl AuthOperation for BinaryProto { 1277 | fn list_mechanisms(&mut self) -> MemCachedResult> { 1278 | let opaque = fastrand::u32(..); 1279 | debug!("List mechanisms"); 1280 | let req_header = RequestHeader::new(Command::SaslListMechanisms, DataType::RawBytes, 0, opaque, 0, 0, 0, 0); 1281 | let req_packet = RequestPacketRef::new(&req_header, &[], &[], &[]); 1282 | req_packet.write_to(&mut self.stream)?; 1283 | self.stream.flush()?; 1284 | 1285 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1286 | while resp.header.opaque != opaque { 1287 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1288 | resp = ResponsePacket::read_from(&mut self.stream)?; 1289 | } 1290 | 1291 | match resp.header.status { 1292 | Status::NoError => {} 1293 | _ => return Err(From::from(Error::from_status(resp.header.status, None))), 1294 | } 1295 | 1296 | match str::from_utf8(&resp.value[..]) { 1297 | Ok(s) => Ok(s.split(' ').map(|mech| mech.to_string()).collect()), 1298 | Err(..) => Err(proto::Error::OtherError { 1299 | desc: "Mechanism decode error", 1300 | detail: None, 1301 | }), 1302 | } 1303 | } 1304 | 1305 | fn auth_start(&mut self, mech: &str, init: &[u8]) -> MemCachedResult { 1306 | let opaque = fastrand::u32(..); 1307 | debug!("Auth start, mechanism: {:?}, init: {:?}", mech, init); 1308 | let req_header = RequestHeader::from_payload( 1309 | Command::SaslAuthenticate, 1310 | DataType::RawBytes, 1311 | 0, 1312 | opaque, 1313 | 0, 1314 | mech.as_bytes(), 1315 | &[], 1316 | init, 1317 | ); 1318 | let req_packet = RequestPacketRef::new(&req_header, &[], mech.as_bytes(), init); 1319 | req_packet.write_to(&mut self.stream)?; 1320 | self.stream.flush()?; 1321 | 1322 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1323 | while resp.header.opaque != opaque { 1324 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1325 | resp = ResponsePacket::read_from(&mut self.stream)?; 1326 | } 1327 | 1328 | match resp.header.status { 1329 | Status::AuthenticationFurtherStepRequired => Ok(AuthResponse::Continue(resp.value.to_vec())), 1330 | Status::NoError => Ok(AuthResponse::Succeeded), 1331 | Status::AuthenticationRequired => Ok(AuthResponse::Failed), 1332 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1333 | } 1334 | } 1335 | 1336 | fn auth_continue(&mut self, mech: &str, data: &[u8]) -> MemCachedResult { 1337 | let opaque = fastrand::u32(..); 1338 | debug!("Auth continue, mechanism: {:?}, data: {:?}", mech, data); 1339 | let req_header = RequestHeader::from_payload( 1340 | Command::SaslStep, 1341 | DataType::RawBytes, 1342 | 0, 1343 | opaque, 1344 | 0, 1345 | mech.as_bytes(), 1346 | &[], 1347 | data, 1348 | ); 1349 | let req_packet = RequestPacketRef::new(&req_header, &[], mech.as_bytes(), data); 1350 | req_packet.write_to(&mut self.stream)?; 1351 | self.stream.flush()?; 1352 | 1353 | let mut resp = ResponsePacket::read_from(&mut self.stream)?; 1354 | while resp.header.opaque != opaque { 1355 | debug!("Expecting opaque: {} but got {}, trying again ...", opaque, resp.header.opaque); 1356 | resp = ResponsePacket::read_from(&mut self.stream)?; 1357 | } 1358 | 1359 | match resp.header.status { 1360 | Status::AuthenticationFurtherStepRequired => Ok(AuthResponse::Continue(resp.value.to_vec())), 1361 | Status::NoError => Ok(AuthResponse::Succeeded), 1362 | Status::AuthenticationRequired => Ok(AuthResponse::Failed), 1363 | _ => Err(From::from(Error::from_status(resp.header.status, None))), 1364 | } 1365 | } 1366 | } 1367 | 1368 | #[cfg(test)] 1369 | mod test { 1370 | use crate::proto::{BinaryProto, CasOperation, MultiOperation, NoReplyOperation, Operation, ServerOperation}; 1371 | use std::collections::{BTreeMap, HashMap}; 1372 | use std::net::TcpStream; 1373 | 1374 | use bufstream::BufStream; 1375 | 1376 | const SERVER_ADDR: &str = "127.0.0.1:11211"; 1377 | 1378 | fn get_client() -> BinaryProto> { 1379 | let stream = TcpStream::connect(SERVER_ADDR).unwrap(); 1380 | BinaryProto::new(BufStream::new(stream)) 1381 | } 1382 | 1383 | #[test] 1384 | fn test_set_get_delete() { 1385 | const KEY: &[u8] = b"test:set_get_delete"; 1386 | const VAL: &[u8] = b"world"; 1387 | 1388 | let mut client = get_client(); 1389 | client.set(KEY, VAL, 0xdead_beef, 120).unwrap(); 1390 | 1391 | let get_resp = client.get(KEY); 1392 | assert_eq!(get_resp.unwrap(), (VAL.to_vec(), 0xdead_beef)); 1393 | 1394 | let getk_resp = client.getk(KEY); 1395 | assert_eq!(getk_resp.unwrap(), (KEY.to_vec(), VAL.to_vec(), 0xdead_beef)); 1396 | 1397 | client.delete(KEY).unwrap(); 1398 | } 1399 | 1400 | #[test] 1401 | fn test_incr_decr() { 1402 | const KEY: &[u8] = b"test:incr_decr"; 1403 | 1404 | let mut client = get_client(); 1405 | let _ = client.delete(KEY); 1406 | { 1407 | let incr_resp = client.increment(KEY, 1, 0, 120); 1408 | assert_eq!(incr_resp.unwrap(), 0); 1409 | } 1410 | 1411 | { 1412 | let incr_resp = client.increment(KEY, 10, 0, 120); 1413 | assert_eq!(incr_resp.unwrap(), 10); 1414 | } 1415 | 1416 | { 1417 | let decr_resp = client.decrement(KEY, 5, 0, 120); 1418 | assert_eq!(decr_resp.unwrap(), 5); 1419 | } 1420 | 1421 | { 1422 | let decr_resp = client.decrement(KEY, 20, 0, 120); 1423 | assert_eq!(decr_resp.unwrap(), 0); 1424 | } 1425 | 1426 | client.delete(KEY).unwrap(); 1427 | } 1428 | 1429 | #[test] 1430 | fn test_version() { 1431 | let mut client = get_client(); 1432 | client.version().unwrap(); 1433 | } 1434 | 1435 | #[test] 1436 | fn test_noop() { 1437 | let mut client = get_client(); 1438 | client.noop().unwrap(); 1439 | } 1440 | 1441 | #[test] 1442 | #[should_panic] 1443 | fn test_quit() { 1444 | let mut client = get_client(); 1445 | client.quit().unwrap(); 1446 | 1447 | client.noop().unwrap(); 1448 | } 1449 | 1450 | #[test] 1451 | fn test_flush() { 1452 | let mut client = get_client(); 1453 | client.flush(2).unwrap(); 1454 | } 1455 | 1456 | #[test] 1457 | fn test_add() { 1458 | const KEY: &[u8] = b"test:add"; 1459 | const INIT_VAL: &[u8] = b"initial"; 1460 | const ADD_VAL: &[u8] = b"added"; 1461 | 1462 | let mut client = get_client(); 1463 | 1464 | let _ = client.delete(KEY); 1465 | 1466 | client.add(KEY, INIT_VAL, 0xdead_beef, 120).unwrap(); 1467 | 1468 | { 1469 | let get_resp = client.get(KEY); 1470 | assert_eq!(get_resp.unwrap(), (INIT_VAL.to_vec(), 0xdead_beef)); 1471 | client.add(KEY, ADD_VAL, 0xdead_beef, 120).unwrap_err(); 1472 | } 1473 | 1474 | client.delete(KEY).unwrap(); 1475 | } 1476 | 1477 | #[test] 1478 | fn test_replace() { 1479 | let mut client = get_client(); 1480 | client 1481 | .replace(b"test:replace_key", b"replaced", 0xdead_beef, 120) 1482 | .unwrap_err(); 1483 | client.add(b"test:replace_key", b"just_add", 0xdead_beef, 120).unwrap(); 1484 | client 1485 | .replace(b"test:replace_key", b"replaced", 0xdead_beef, 120) 1486 | .unwrap(); 1487 | client.delete(b"test:replace_key").unwrap(); 1488 | } 1489 | 1490 | #[test] 1491 | fn test_append_prepend() { 1492 | let mut client = get_client(); 1493 | client.append(b"test:append_key", b"appended").unwrap_err(); 1494 | client.prepend(b"test:append_key", b"prepended").unwrap_err(); 1495 | 1496 | { 1497 | client.add(b"test:append_key", b"just_add", 0xdead_beef, 120).unwrap(); 1498 | client.append(b"test:append_key", b"appended").unwrap(); 1499 | 1500 | let get_resp = client.get(b"test:append_key"); 1501 | assert_eq!(get_resp.unwrap(), (b"just_addappended".to_vec(), 0xdead_beef)); 1502 | 1503 | client.prepend(b"test:append_key", b"prepended").unwrap(); 1504 | let get_resp = client.get(b"test:append_key"); 1505 | assert_eq!(get_resp.unwrap(), (b"prependedjust_addappended".to_vec(), 0xdead_beef)); 1506 | } 1507 | 1508 | client.delete(b"test:append_key").unwrap(); 1509 | } 1510 | 1511 | #[test] 1512 | fn test_stat() { 1513 | let mut client = get_client(); 1514 | client.stat().unwrap(); 1515 | } 1516 | 1517 | #[test] 1518 | fn test_touch() { 1519 | let mut client = get_client(); 1520 | client.touch(b"test:touch", 120).unwrap_err(); 1521 | client.add(b"test:touch", b"val", 0xcafe_babe, 100).unwrap(); 1522 | client.touch(b"test:touch", 120).unwrap(); 1523 | client.delete(b"test:touch").unwrap(); 1524 | } 1525 | 1526 | #[test] 1527 | fn test_set_get_delete_incr_muti() { 1528 | let mut client = get_client(); 1529 | 1530 | let mut data = BTreeMap::new(); 1531 | data.insert(&b"test:multi_hello1"[..], (&b"world1"[..], 0xdead_beef, 120)); 1532 | data.insert(&b"test:multi_hello2"[..], (&b"world2"[..], 0xdead_beef, 120)); 1533 | data.insert(&b"test:multi_num1"[..], (&b"100"[..], 0xdead_beef, 120)); 1534 | data.insert(&b"test:multi_num2"[..], (&b"200"[..], 0xdead_beef, 120)); 1535 | data.insert(&b"test:multi_lastone"[..], (&b"last!"[..], 0xdead_beef, 120)); 1536 | 1537 | client.set_multi(data).unwrap(); 1538 | 1539 | let get_resp_map = client 1540 | .get_multi(&[b"test:multi_hello1", b"test:multi_hello2", b"test:multi_lastone"]) 1541 | .unwrap(); 1542 | assert_eq!(get_resp_map.get(&b"test:multi_hello1".to_vec()), Some(&(b"world1".to_vec(), 0xdead_beef))); 1543 | assert_eq!(get_resp_map.get(&b"test:multi_hello2".to_vec()), Some(&(b"world2".to_vec(), 0xdead_beef))); 1544 | assert_eq!(get_resp_map.get(&b"test:multi_lastone".to_vec()), Some(&(b"last!".to_vec(), 0xdead_beef))); 1545 | 1546 | client 1547 | .delete_multi(&[b"test:multi_hello1", b"test:multi_hello2", b"test:multi_num3"]) 1548 | .unwrap(); 1549 | 1550 | let get_resp_map = client 1551 | .get_multi(&[b"test:multi_hello1", b"test:multi_hello2", b"test:multi_lastone"]) 1552 | .unwrap(); 1553 | assert_eq!(get_resp_map.get(&b"test:multi_hello1".to_vec()), None); 1554 | assert_eq!(get_resp_map.get(&b"test:multi_hello2".to_vec()), None); 1555 | assert_eq!(get_resp_map.get(&b"test:multi_lastone".to_vec()), Some(&(b"last!".to_vec(), 0xdead_beef))); 1556 | 1557 | let mut data = HashMap::new(); 1558 | data.insert(&b"test:multi_num1"[..], (10, 50, 120)); 1559 | data.insert(&b"test:multi_num2"[..], (20, 50, 120)); 1560 | data.insert(&b"test:multi_num3"[..], (30, 50, 120)); 1561 | client.increment_multi(data).unwrap(); 1562 | 1563 | let get_resp_map = client 1564 | .get_multi(&[b"test:multi_num1", b"test:multi_num2", b"test:multi_num3"]) 1565 | .unwrap(); 1566 | assert_eq!(get_resp_map.get(&b"test:multi_num1".to_vec()), Some(&(b"110".to_vec(), 0xdead_beef))); 1567 | assert_eq!(get_resp_map.get(&b"test:multi_num2".to_vec()), Some(&(b"220".to_vec(), 0xdead_beef))); 1568 | assert_eq!(get_resp_map.get(&b"test:multi_num3".to_vec()), Some(&(b"50".to_vec(), 0x0))); 1569 | 1570 | client.delete_multi(&[b"lastone", b"not_exists!!!!"]).unwrap(); 1571 | } 1572 | 1573 | #[test] 1574 | fn test_set_add_replace_noreply() { 1575 | let key = b"test:noreply_key"; 1576 | let set_val = b"value"; 1577 | let add_val = b"just add"; 1578 | let rep_val = b"replaced"; 1579 | 1580 | let mut client = get_client(); 1581 | 1582 | let _ = client.delete(key); 1583 | 1584 | client.add_noreply(key, add_val, 0xdead_beef, 120).unwrap(); 1585 | 1586 | let get_resp = client.get(key); 1587 | assert_eq!(get_resp.unwrap(), (add_val.to_vec(), 0xdead_beef)); 1588 | 1589 | client.set_noreply(key, set_val, 0xdead_beef, 120).unwrap(); 1590 | 1591 | let get_resp = client.get(key); 1592 | assert_eq!(get_resp.unwrap(), (set_val.to_vec(), 0xdead_beef)); 1593 | 1594 | client.replace_noreply(key, rep_val, 0xcafe_babe, 120).unwrap(); 1595 | 1596 | let get_resp = client.get(key); 1597 | assert_eq!(get_resp.unwrap(), (rep_val.to_vec(), 0xcafe_babe)); 1598 | 1599 | client.delete(key).unwrap(); 1600 | } 1601 | 1602 | #[test] 1603 | fn test_set_add_replace_cas() { 1604 | let key = b"test:cas_key"; 1605 | let set_val = b"value"; 1606 | let add_val = b"just add"; 1607 | let rep_val = b"replaced"; 1608 | 1609 | let mut client = get_client(); 1610 | 1611 | let add_cas = client.add_cas(key, add_val, 0xdead_beef, 120).unwrap(); 1612 | 1613 | { 1614 | client.set_cas(key, set_val, 0xdead_beef, 120, add_cas + 1).unwrap_err(); 1615 | 1616 | let (_, _, get_cas) = client.get_cas(key).unwrap(); 1617 | assert_eq!(get_cas, add_cas); 1618 | 1619 | client 1620 | .replace_cas(key, rep_val, 0xdead_beef, 120, add_cas + 1) 1621 | .unwrap_err(); 1622 | } 1623 | 1624 | { 1625 | let set_cas = client.set_cas(key, set_val, 0xdead_beef, 120, add_cas).unwrap(); 1626 | let (_, _, get_cas) = client.get_cas(key).unwrap(); 1627 | assert_eq!(get_cas, set_cas); 1628 | 1629 | client.replace_cas(key, rep_val, 0xdead_beef, 120, set_cas).unwrap(); 1630 | } 1631 | 1632 | client.delete(key).unwrap(); 1633 | } 1634 | 1635 | #[test] 1636 | fn test_incr_decr_cas() { 1637 | let key = b"test:incr_decr_cas"; 1638 | let mut client = get_client(); 1639 | 1640 | let _ = client.delete(key); 1641 | 1642 | let (_, incr_cas) = client.increment_cas(key, 0, 100, 120, 0).unwrap(); 1643 | 1644 | client.increment_cas(key, 0, 10, 120, incr_cas + 1).unwrap_err(); 1645 | 1646 | let (_, incr_cas) = client.increment_cas(key, 0, 10, 120, incr_cas).unwrap(); 1647 | 1648 | client.decrement_cas(key, 0, 10, 120, incr_cas + 1).unwrap_err(); 1649 | client.decrement_cas(key, 0, 10, 120, incr_cas).unwrap(); 1650 | 1651 | client.delete(key).unwrap(); 1652 | } 1653 | 1654 | #[test] 1655 | fn test_append_prepend_cas() { 1656 | const KEY: &[u8] = b"test:append_prepend_cas"; 1657 | let mut client = get_client(); 1658 | 1659 | let _ = client.delete(KEY); 1660 | 1661 | let set_cas = client.set_cas(KEY, b"appended", 0, 120, 0).unwrap(); 1662 | client.append_cas(KEY, b"appended", set_cas + 1).unwrap_err(); 1663 | 1664 | let ap_cas = client.append_cas(KEY, b"appended", set_cas).unwrap(); 1665 | client.prepend_cas(KEY, b"prepend", ap_cas + 1).unwrap_err(); 1666 | client.prepend_cas(KEY, b"prepend", ap_cas).unwrap(); 1667 | 1668 | client.delete(KEY).unwrap(); 1669 | } 1670 | 1671 | #[test] 1672 | fn test_if_noreply_failed() { 1673 | let key = b"test:noreply_fail_key"; 1674 | let set_val = b"value"; 1675 | let add_val = b"just add"; 1676 | 1677 | let mut client = get_client(); 1678 | 1679 | let _ = client.delete(key); 1680 | 1681 | client.set_noreply(key, set_val, 0xdead_beef, 120).unwrap(); 1682 | 1683 | // Should failed, because key is already set 1684 | client.add_noreply(key, add_val, 0xdead_beef, 120).unwrap(); 1685 | 1686 | let get_resp = client.get(key); 1687 | assert_eq!(get_resp.unwrap(), (set_val.to_vec(), 0xdead_beef)); 1688 | } 1689 | } 1690 | --------------------------------------------------------------------------------