Formal terrapipe query parsing

next
Sayan Nandan 4 years ago
parent a059f8b3e4
commit 3740b53efd
No known key found for this signature in database
GPG Key ID: C31EFD7DDA12AEE0

@ -615,3 +615,6 @@ reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS

@ -19,16 +19,56 @@
*
*/
const METAFRAME_PROTOCOL_TAG: &'static str = "TP";
const METAFRAME_QUERY_TAG: &'static str = "Q";
const METAFRAME_QUERY_SET_TAG: &'static str = "SET";
const METAFRAME_QUERY_GET_TAG: &'static str = "GET";
const METAFRAME_QUERY_UPDATE_TAG: &'static str = "UPDATE";
const METAFRAME_QUERY_DEL_TAG: &'static str = "DEL";
//! This is the implementation of the terrabasedb/RFC#1
const MF_PROTOCOL_TAG: &'static str = "TP";
const MF_QUERY_TAG: &'static str = "Q";
const MF_QUERY_SET_TAG: &'static str = "SET";
const MF_QUERY_GET_TAG: &'static str = "GET";
const MF_QUERY_UPDATE_TAG: &'static str = "UPDATE";
const MF_QUERY_DEL_TAG: &'static str = "DEL";
macro_rules! result_packet {
($version:expr, $respcode:expr, $data:expr) => {{
let data = $data.to_string();
format!(
"TP/{}/R/{}/{}\n{}",
$version.to_string(),
$respcode,
data.len(),
$data
)
}};
}
macro_rules! query_packet {
($version:expr, $querytype:expr, $data:expr) => {
format!(
"TP/{}/Q/{}\n{}",
$version.to_string(),
$querytype.to_string(),
$data
)
};
}
/// Anything that implements `ToString` automatically implements `ToTPArgs`
pub trait ToTPArgs: ToString {
fn to_tp_args(&self) -> String;
}
/// Minimal representation of _semver_
#[derive(Debug)]
pub struct Version(u8, u8, u8);
impl ToString for Version {
fn to_string(&self) -> String {
format!("{}.{}.{}", self.0, self.1, self.2)
}
}
impl Version {
/// Parse a new semver using a string in the form x.y.z
pub fn new_from_str<'a>(val: &'a str) -> Option<Self> {
let vals: Vec<&str> = val.split(".").collect();
if vals.len() != 3 {
@ -45,6 +85,7 @@ impl Version {
return None;
}
}
/// Use semver to check if the versions are compatible with each other
pub fn is_compatible_with(&self, other: &Version) -> bool {
if self.0 == other.0 {
true
@ -54,138 +95,172 @@ impl Version {
}
}
/// `Key` is a type alias for `String`
type Key = String;
/// `Value` is a type alias for `String`
type Value = String;
pub enum TPQueryType {
/// A fully parsed and ready-to-execute Query action
#[derive(Debug, PartialEq)]
pub enum TPQueryMethod {
GET(Key),
SET(Key, Key),
UPDATE(Key, Key),
DEL(Key, Key),
SET(Key, Value),
UPDATE(Key, Value),
DEL(Key),
}
/// Representation of query types
#[derive(Debug, PartialEq)]
pub enum TPQueryType {
GET,
SET,
UPDATE,
DEL,
}
impl ToString for TPQueryType {
fn to_string(&self) -> String {
use TPQueryType::*;
if self == &GET {
return MF_QUERY_GET_TAG.to_owned();
} else if self == &SET {
return MF_QUERY_SET_TAG.to_owned();
} else if self == &UPDATE {
return MF_QUERY_UPDATE_TAG.to_owned();
} else {
return MF_QUERY_DEL_TAG.to_owned();
}
}
}
/// Errors that may occur while parsing a query packet
#[derive(Debug, PartialEq)]
pub enum TPQueryError {
/// `1: Not Found`
///
/// The target resource could not be found
NotFound,
/// `2: Overwrite Error`
///
/// This usually occurs when a query tries to alter the value
/// of an existing key using `SET` instead of `UPDATE`
OverwriteError,
/// `3: Method Not Allowed`
///
/// The client is trying to do something illegal like sending a `Result`
/// packet instead of a `Query` packet
MethodNotAllowed,
/// `4: Internal Server Error`
///
/// There is an internal server error
InternalServerError,
/// `5: Invalid Metaframe`
///
/// The metaframe of the query packet has some incorrect partitions or
/// has an incorrect format
InvalidMetaframe,
/// `6: Corrupt Dataframe`
///
/// The dataframe may be missing some bytes or more bytes were expected
CorruptDataframe,
/// `7: Protocol Version Mismatch`
///
/// The protocol used by the client is not compatible with the protocol
/// used by the server
ProtocolVersionMismatch,
/// `8: Corrupt Packet`
///
/// The packet is either empty or is missing a newline
CorruptPacket,
}
#[cfg(test)]
#[test]
fn test_result_macros() {
let proto_version = Version(0, 1, 0);
let query = query_packet!(proto_version, TPQueryType::GET, "sayan");
let result = result_packet!(proto_version, 0, 17);
let query_should_be = "TP/0.1.0/Q/GET\nsayan".to_owned();
let result_should_be = "TP/0.1.0/R/0/2\n17".to_owned();
assert_eq!(query, query_should_be);
assert_eq!(result, result_should_be);
}
pub fn parse_query_packet(own_version: Version, packet: &String) {
pub fn parse_query_packet(
packet: String,
self_version: &Version,
) -> Result<TPQueryMethod, TPQueryError> {
let rlines: Vec<&str> = packet.lines().collect();
// This should give us two or more lines
if rlines.len() < 2 {
eprintln!("error: 5 CorruptDataframe");
return;
}
// This is the meta frame
let meta_frame: Vec<&str> = rlines[0].split("/").collect();
if meta_frame.len() != 3 {
eprintln!("error: 4 InvalidMetaframe");
return;
}
// This is the data frame
let data_frame: Vec<&str> = rlines[1].split_whitespace().collect();
// Check if version is valid
let version_header: Vec<&str> = meta_frame[0].split(" ").collect();
if let Some(version_tag) = version_header.get(0) {
if version_tag == &METAFRAME_PROTOCOL_TAG {
if let Some(version) = version_header.get(1) {
if let Some(v) = Version::new_from_str(&version) {
if !v.is_compatible_with(&own_version) {
eprintln!("error: 6 ProtocolVersionMismatch");
} else {
()
return Err(TPQueryError::CorruptPacket);
}
} else {
eprintln!("error: 4 InvalidMetaframe");
let metaframe: Vec<&str> = rlines[0].split("/").collect();
if metaframe.len() != 4 {
return Err(TPQueryError::InvalidMetaframe);
}
} else {
eprintln!("error: 4 InvalidMetaframe");
if metaframe[0] != MF_PROTOCOL_TAG {
return Err(TPQueryError::InvalidMetaframe);
}
if let Some(v) = Version::new_from_str(metaframe[1]) {
if self_version.is_compatible_with(&v) {
()
} else {
eprintln!("error: 4 InvalidMetaframe");
return Err(TPQueryError::ProtocolVersionMismatch);
}
} else {
eprintln!("error: 4 InvalidMetaframe");
}
// Now get request type
let request_partition: Vec<&str> = meta_frame[1].split(" ").collect();
if let Some(request_tag) = request_partition.get(0) {
if request_tag == &METAFRAME_QUERY_TAG {
if let Some(qtype) = request_partition.get(1) {
match qtype {
&METAFRAME_QUERY_SET_TAG => {
// This is a set request
if let Some(key) = data_frame.get(0) {
if let Some(value) = data_frame.get(1) {
if data_frame.get(2).is_none() {
println!("SET {} {}", key, value);
} else {
eprintln!("error: 5 Corrupt Dataframe");
return;
}
} else {
eprintln!("error: 5 Corrupt Dataframe");
return;
if metaframe[2] != MF_QUERY_TAG {
return Err(TPQueryError::InvalidMetaframe);
}
} else {
eprintln!("error: 5 Corrupt Dataframe");
return;
let dataframe: Vec<&str> = rlines[1].split_whitespace().collect();
if dataframe.len() == 0 {
return Err(TPQueryError::CorruptDataframe);
}
match metaframe[3] {
MF_QUERY_GET_TAG => {
// This is a GET query
if let Some(key) = dataframe.get(0) {
if dataframe.get(1).is_none() {
return Ok(TPQueryMethod::GET(key.to_string()));
}
&METAFRAME_QUERY_UPDATE_TAG => {
// This is an update request
if let Some(key) = data_frame.get(0) {
if let Some(value) = data_frame.get(1) {
if data_frame.get(2).is_none() {
println!("UPDATE {} {}", key, value);
} else {
eprintln!("error: 5 Corrupt Dataframe");
return;
}
} else {
eprintln!("error: 5 Corrupt Dataframe");
return;
}
} else {
eprintln!("error: 5 Corrupt Dataframe");
return;
MF_QUERY_SET_TAG => {
// This is a SET query
if let Some(key) = dataframe.get(0) {
if let Some(value) = dataframe.get(1) {
return Ok(TPQueryMethod::SET(key.to_string(), value.to_string()));
}
}
&METAFRAME_QUERY_GET_TAG => {
// This is a get request
if let Some(key) = data_frame.get(0) {
if data_frame.get(1).is_none() {
println!("GET {}", key);
} else {
eprintln!("error: 5 Corrupt Dataframe");
return;
}
} else {
eprintln!("error: 5 Corrupt Dataframe");
return;
MF_QUERY_UPDATE_TAG => {
// This is a SET query
if let Some(key) = dataframe.get(0) {
if let Some(value) = dataframe.get(1) {
return Ok(TPQueryMethod::UPDATE(key.to_string(), value.to_string()));
}
}
_ => {
eprintln!("error: 4 Invalid Metaframe");
return;
}
MF_QUERY_DEL_TAG => {
// This is a DEL query
if let Some(key) = dataframe.get(0) {
if dataframe.get(1).is_none() {
return Ok(TPQueryMethod::DEL(key.to_string()));
}
} else {
eprintln!("error: 5 Corrupt dataframe");
return;
}
} else {
eprintln!("error: 5 Corrupt dataframe");
return;
}
} else {
eprintln!("error: 5 Corrupt dataframe");
return;
// Some random illegal command
_ => return Err(TPQueryError::MethodNotAllowed),
}
Err(TPQueryError::CorruptDataframe)
}
#[cfg(test)]
#[test]
fn test_parse_header_query() {
let query_packet_get = "TP 0.1.1/Q GET/5\nsayan".to_owned();
parse_query_packet(Version(0, 1, 1), &query_packet_get);
let query_packet_set = "TP 0.1.1/Q SET/8\nsayan 18".to_owned();
parse_query_packet(Version(0, 1, 1), &query_packet_set);
let erroring_packet_set = "TP 0.1.1/Q SET/13\nhi sayan".to_owned();
parse_query_packet(Version(0, 1, 1), &erroring_packet_set);
fn test_query_packet_parsing() {
let qpacket = query_packet!(Version(0, 1, 0), TPQueryType::GET, "sayan");
let query_should_be = TPQueryMethod::GET("sayan".to_owned());
let parsed_qpacket = parse_query_packet(qpacket, &Version(0, 1, 0)).unwrap();
assert_eq!(query_should_be, parsed_qpacket);
}

@ -17,8 +17,7 @@ TP makes use of two packets:
The `Q` uery packet has the following structure:
```
TP <VERSION>/Q <QTYPE>/<LENGTH>
\n
TP/<VERSION>/Q/<QTYPE>
--------------- DATA ----------
```
@ -27,75 +26,77 @@ TP <VERSION>/Q <QTYPE>/<LENGTH>
### Line 1: Meta frame
The first line is called the meta frame. The `<VALUES>` and their corresponding meanings are as follows:
- **`VERSION`**: The version of the protocol, in semver form, i.e `major.minor.patch`.
* **`VERSION`**: The version of the protocol, in semver form, i.e `major.minor.patch` .
An example can be: `0.1.0`
- **`QTYPE`**: This is the type of query. It can have the following values:
- `GET`: For `GET` operations
- `SET`: For `SET` operations
- `UPDATE`: For `UDPATE` operations
- `DEL`: For `DEL` operations
- **`LENGTH`**: The number of bytes that are being transmitted. This is useful for preallocating buffers for copying the data.
* **`QTYPE`**: This is the type of query. It can have the following values:
- `GET` : For `GET` operations
- `SET` : For `SET` operations
- `UPDATE` : For `UDPATE` operations
- `DEL` : For `DEL` operations
#### Example meta frame
```
TP 0.1.0/Q GET/15
TP/0.1.0/Q/GET
```
### Line 2: Line break
This is a line break that separates the meta frame from the data frame.
### Line 2 and subsequent lines: Data frame
### Line 3: Data frame
The data frame doesn't have any defined format. It can be anything that can be transferred over TCP - that is, well, anything: letters, numbers or vaguely bytes.
## The `R`esult packet
The `R`esult packet has the following structure:
## The `R` esult packet
The `R` esult packet has the following structure:
```
TP <VERSION>/R <QTYPE>/<RESPONSECODE>/<LENGTH>
\n
TP/<VERSION>/R/<RESPONSECODE>/<LENGTH>
--------------------- DATA -------------------
```
**Note:** The first line is followed by a line break, and then the subsequent lines.
### Line 1: Meta frame
Just like the `Q`uery packet, the first line is called the meta frame.
Just like the `Q` uery packet, the first line is called the meta frame.
The `<VALUES>` and their corresponding meanings are as follows:
- **`VERSION`**: The version of the protocol, in semver form, i.e `major.minor.patch`.
* **`VERSION`**: The version of the protocol, in semver form, i.e `major.minor.patch` .
An example can be: `0.1.0`
- **`QTYPE`**: This is the type of query. It can have the following values:
- `GET`: For responses to `GET` operations
- `SET`: For responses to `SET` operations
- `UPDATE`: For responses to `UPDATE` operations
- `DEL`: For response to `DEL` operations
This must match with the initial query packet.
- **`RESPONSECODE`**: This is the outcome of the query. It can have the following values:
* **`RESPONSECODE`**: This is the outcome of the query. It can have the following values:
- 0: Okay
- 1: Not found
- 2: Method not allowed
- 3: Server error
- 4: Corrupt byte
- 5: Protocol version mismatch
- **`LENGTH`**: The number of bytes that are being transmitted. This is useful for preallocating buffers for copying the data.
* **`LENGTH`**: The number of bytes that are being transmitted. This is useful for preallocating buffers for copying the data.
#### Example data frame
```
sayan is writing a protocol
```
## An example of a query/result
Let us assume a key called `sayan` with a value of '17' exists on the database.
Our client, uses `0.1.0` version of tp and sends a `GET` request for the key to our server which also uses version `0.1.0` of tp.
### The `Q`uery packet
### The `Q` uery packet
```
TP 0.1.0/Q GET/5
\n
TP/0.1.0/Q/GET
sayan
```
### The `R`esult packet
### The `R` esult packet
```
TP 0.1.0/R GET/0/2
\n
TP/0.1.0/R/0/2
17
```
Loading…
Cancel
Save