#![macro_use]
#![allow(non_camel_case_types)]
use std::slice::Iter;
use unicode_segmentation::UnicodeSegmentation;
use lifeguard::Pool;
#[derive(Clone)]
pub struct HeaderList {
pub headers: Vec<Header>
}
impl HeaderList {
pub fn new() -> HeaderList {
HeaderList::with_capacity(0)
}
pub fn with_capacity(capacity: usize) -> HeaderList {
HeaderList {
headers: Vec::with_capacity(capacity)
}
}
pub fn push(&mut self, header: Header) {
self.headers.push(header);
}
pub fn pop(&mut self) -> Option<Header> {
self.headers.pop()
}
pub fn iter<'a>(&'a self) -> Iter<'a, Header> {
self.headers.iter()
}
pub fn drain<F>(&mut self, mut sink: F) where F : FnMut(Header) {
while let Some(header) = self.headers.pop() {
sink(header);
}
}
pub fn concat(&mut self, other_list: &mut HeaderList) {
other_list.headers.reverse();
while let Some(header) = other_list.pop() {
self.headers.push(header);
}
}
pub fn retain<F>(&mut self, test: F) where F : Fn(&Header)->bool {
self.headers.retain(test)
}
}
pub struct SuppressedHeader<'a> (pub &'a str);
pub struct ContentType<'a>(pub &'a str);
#[derive(Clone)]
pub struct Header {
buffer : String,
delimiter_index : u32
}
pub struct HeaderCodec {
strings : Pool<String>
}
impl HeaderCodec {
pub fn new() -> HeaderCodec {
use std::usize;
HeaderCodec::with_pool_size_and_max(0, usize::max_value())
}
pub fn recycle(&mut self, header: Header) {
debug!("Recycling Header: {}", header.buffer);
self.strings.attach(header.buffer);
}
pub fn with_pool_size_and_max(size: usize, max_size: usize) -> HeaderCodec {
HeaderCodec {
strings: Pool::with_size_and_max(size, max_size)
}
}
pub fn encode_key_value(&mut self, key: &str, value: &str) -> Header {
let mut raw_string = self.encode_value(key);
let encoded_value = self.encode_value(value);
raw_string.push_str(":");
raw_string.push_str(&encoded_value);
self.strings.attach(encoded_value);
Header {
buffer: raw_string,
delimiter_index: key.len() as u32
}
}
fn encode_value(&mut self, value: &str) -> String {
let mut encoded = self.strings.detached();
for grapheme in UnicodeSegmentation::graphemes(value, true) {
match grapheme {
"\\" => encoded.push_str(r"\\"), "\r" => encoded.push_str(r"\r"),
"\n" => encoded.push_str(r"\n"),
":" => encoded.push_str(r"\c"),
g => encoded.push_str(g)
}
}
encoded
}
pub fn decode(&mut self, raw_string: &str) -> Option<Header> {
let string = self.strings.new_from(raw_string).detach();
self.decode_string(string)
}
pub fn decode_string(&mut self, raw_string: String) -> Option<Header> {
let delimiter_index = match raw_string.find(':') {
Some(index) => index,
None => return None
};
let tmp_header = Header{
buffer: raw_string,
delimiter_index: delimiter_index as u32
};
let header = self.decode_key_value(tmp_header.get_key(), tmp_header.get_value());
self.recycle(tmp_header);
Some(header)
}
pub fn decode_key_value(&mut self, key: &str, value: &str) -> Header {
let mut raw_string = self.decode_value(key);
let decoded_value = self.decode_value(value);
raw_string.push_str(":");
raw_string.push_str(&decoded_value);
self.strings.attach(decoded_value);
Header {
buffer: raw_string,
delimiter_index: key.len() as u32
}
}
fn decode_value(&mut self, value: &str) -> String {
let mut is_escaped = false;
let mut decoded = self.strings.detached();
for grapheme in UnicodeSegmentation::graphemes(value, true) {
if !is_escaped {
match grapheme {
r"\" => is_escaped = true,
g => decoded.push_str(g)
}
continue;
}
match grapheme {
r"c" => decoded.push_str(":"),
r"r" => decoded.push_str("\r"),
r"n" => decoded.push_str("\n"),
r"\" => decoded.push_str("\\"),
g => panic!("Unrecognized escape sequence encountered: '\\{}'.", g)
}
is_escaped = false;
}
decoded
}
}
impl Header {
pub fn new(key: &str, value: &str) -> Header {
HeaderCodec::new().encode_key_value(key, value)
}
pub fn decode(raw_string: &str) -> Option<Header> {
HeaderCodec::new().decode(raw_string)
}
pub fn encode_key_value(key: &str, value: &str) -> Header {
HeaderCodec::new().encode_key_value(key, value)
}
pub fn decode_key_value(key: &str, value: &str) -> Header {
HeaderCodec::new().decode_key_value(key, value)
}
#[allow(dead_code)]
fn decode_value(value: &str) -> String {
HeaderCodec::new().decode_value(value)
}
#[allow(dead_code)]
fn encode_value(value: &str) -> String {
HeaderCodec::new().encode_value(value)
}
pub fn get_raw<'a>(&'a self) -> &'a str {
self.buffer.as_ref()
}
pub fn get_key<'a>(&'a self) -> &'a str {
let index : usize = self.delimiter_index as usize;
&self.buffer[..index]
}
pub fn get_value<'a>(&'a self) -> &'a str {
let index : usize = self.delimiter_index as usize + 1;
&self.buffer[index..]
}
}
pub struct AcceptVersion(pub Vec<StompVersion>);
pub struct Ack<'a>(pub &'a str);
#[derive(Clone, Copy)]
pub struct ContentLength(pub u32);
pub struct Custom(pub Header);
pub struct Destination<'a> (pub &'a str);
#[derive(Clone, Copy)]
pub struct HeartBeat(pub u32, pub u32);
pub struct Host<'a>(pub &'a str);
pub struct Id<'a>(pub &'a str);
pub struct Login<'a>(pub &'a str);
pub struct MessageId<'a>(pub &'a str);
pub struct Passcode<'a>(pub &'a str);
pub struct Receipt<'a>(pub &'a str);
pub struct ReceiptId<'a>(pub &'a str);
pub struct Server<'a>(pub &'a str);
pub struct Session<'a> (pub &'a str);
pub struct Subscription<'a>(pub &'a str);
pub struct Transaction<'a>(pub &'a str);
#[derive(Clone, Copy)]
pub struct Version(pub StompVersion);
#[derive(Clone, Copy)]
pub enum StompVersion {
Stomp_v1_0,
Stomp_v1_1,
Stomp_v1_2,
}
pub trait StompHeaderSet {
fn get_content_length(&self) -> Option<ContentLength>;
fn get_header<'a>(&'a self, key: &str) -> Option<&'a Header>;
fn get_accept_version<'a>(&'a self) -> Option<Vec<StompVersion>>;
fn get_ack<'a>(&'a self) -> Option<Ack<'a>>;
fn get_destination<'a>(&'a self) -> Option<Destination<'a>>;
fn get_heart_beat(&self) -> Option<HeartBeat>;
fn get_host<'a>(&'a self) -> Option<Host<'a>>;
fn get_id<'a>(&'a self) -> Option<Id<'a>>;
fn get_login<'a>(&'a self) -> Option<Login<'a>>;
fn get_message_id<'a>(&'a self) -> Option<MessageId<'a>>;
fn get_passcode<'a>(&'a self) -> Option<Passcode<'a>>;
fn get_receipt<'a>(&'a self) -> Option<Receipt<'a>>;
fn get_receipt_id<'a>(&'a self) -> Option<ReceiptId<'a>>;
fn get_server<'a>(&'a self) -> Option<Server<'a>>;
fn get_session<'a>(&'a self) -> Option<Session<'a>>;
fn get_subscription<'a>(&'a self) -> Option<Subscription<'a>>;
fn get_transaction<'a>(&'a self) -> Option<Transaction<'a>>;
fn get_version(&self) -> Option<Version>;
}
impl StompHeaderSet for HeaderList {
fn get_header<'a>(&'a self, key: &str) -> Option<&'a Header>{
self.headers.iter().find(|header|
match **header {
ref h if h.get_key() == key => true,
_ => false
}
)
}
fn get_accept_version(&self) -> Option<Vec<StompVersion>> {
let versions : &str = match self.get_header("accept-version") {
Some(h) => h.get_value(),
None => return None
};
let versions: Vec<StompVersion> = versions.split(',').filter_map(|v| match v.trim() {
"1.0" => Some(StompVersion::Stomp_v1_0),
"1.1" => Some(StompVersion::Stomp_v1_1),
"1.2" => Some(StompVersion::Stomp_v1_2),
_ => None
}).collect();
Some(versions)
}
fn get_ack<'a>(&'a self) -> Option<Ack<'a>> {
match self.get_header("ack") {
Some(h) => Some(Ack(h.get_value())),
None => None
}
}
fn get_destination<'a>(&'a self) -> Option<Destination<'a>> {
match self.get_header("destination") {
Some(h) => Some(Destination(h.get_value())),
None => return None
}
}
fn get_heart_beat(&self) -> Option<HeartBeat> {
let spec = match self.get_header("heart-beat") {
Some(h) => h.get_value(),
None => return None
};
let spec_list: Vec<u32> = spec.split(',').filter_map(|str_val| str_val.parse::<u32>().ok()).collect();
if spec_list.len() != 2 {
return None;
}
Some(HeartBeat(spec_list[0], spec_list[1]))
}
fn get_host<'a>(&'a self) -> Option<Host<'a>> {
match self.get_header("host") {
Some(h) => Some(Host(h.get_value())),
None => None
}
}
fn get_id<'a>(&'a self) -> Option<Id<'a>> {
match self.get_header("id") {
Some(h) => Some(Id(h.get_value())),
None => None
}
}
fn get_login<'a>(&'a self) -> Option<Login<'a>> {
match self.get_header("login"){
Some(h) => Some(Login(h.get_value())),
None => None
}
}
fn get_message_id<'a>(&'a self) -> Option<MessageId<'a>> {
match self.get_header("message-id"){
Some(h) => Some(MessageId(h.get_value())),
None => None
}
}
fn get_passcode<'a>(&'a self) -> Option<Passcode<'a>> {
match self.get_header("passcode"){
Some(h) => Some(Passcode(h.get_value())),
None => None
}
}
fn get_receipt<'a>(&'a self) -> Option<Receipt<'a>> {
match self.get_header("receipt"){
Some(h) => Some(Receipt(h.get_value())),
None => None
}
}
fn get_receipt_id<'a>(&'a self) -> Option<ReceiptId<'a>> {
match self.get_header("receipt-id"){
Some(h) => Some(ReceiptId(h.get_value())),
None => None
}
}
fn get_server<'a>(&'a self) -> Option<Server<'a>> {
match self.get_header("server"){
Some(h) => Some(Server(h.get_value())),
None => None
}
}
fn get_session<'a>(&'a self) -> Option<Session<'a>> {
match self.get_header("session"){
Some(h) => Some(Session(h.get_value())),
None => None
}
}
fn get_subscription<'a>(&'a self) -> Option<Subscription<'a>> {
match self.get_header("subscription"){
Some(h) => Some(Subscription(h.get_value())),
None => None
}
}
fn get_transaction<'a>(&'a self) -> Option<Transaction<'a>> {
match self.get_header("transaction"){
Some(h) => Some(Transaction(h.get_value())),
None => None
}
}
fn get_version(&self) -> Option<Version> {
let version = match self.get_header("version"){
Some(h) => h.get_value(),
None => return None
};
match (version).as_ref() {
"1.0" => Some(Version(StompVersion::Stomp_v1_0)), "1.1" => Some(Version(StompVersion::Stomp_v1_1)),
"1.2" => Some(Version(StompVersion::Stomp_v1_2)),
_ => None
}
}
fn get_content_length(&self) -> Option<ContentLength> {
let length = match self.get_header("content-length") {
Some(h) => h.get_value(),
None => return None
};
match length.parse::<u32>().ok() {
Some(l) => Some(ContentLength(l)),
None => None
}
}
}
#[macro_export]
macro_rules! header_list [
($($header: expr), *) => ({
let header_list = HeaderList::new();
$(header_list.push($header);)*
header_list
});
($($key:expr => $value: expr), *) => ({
let mut header_list = HeaderList::new();
$(header_list.push(Header::encode_key_value($key, $value));)*
header_list
})
];
#[test]
fn encode_return_carriage() {
let unencoded = "Hello\rWorld";
let encoded = r"Hello\rWorld";
assert!(encoded == Header::encode_value(unencoded));
}
#[test]
fn decode_return_carriage() {
let unencoded = "Hello\rWorld";
let encoded = r"Hello\rWorld";
assert!(unencoded == Header::decode_value(encoded));
}
#[test]
fn encode_newline() {
let unencoded = "Hello\nWorld";
let encoded = r"Hello\nWorld";
assert!(encoded == Header::encode_value(unencoded));
}
#[test]
fn decode_newline() {
let unencoded = "Hello\nWorld";
let encoded = r"Hello\nWorld";
assert!(unencoded == Header::decode_value(encoded));
}
#[test]
fn encode_colon() {
let unencoded = "Hello:World";
let encoded = r"Hello\cWorld";
assert!(encoded == Header::encode_value(unencoded));
}
#[test]
fn decode_colon() {
let unencoded = "Hello:World";
let encoded = r"Hello\cWorld";
assert!(unencoded == Header::decode_value(encoded));
}
#[test]
fn encode_slash() {
let unencoded = r"Hello\World";
let encoded = r"Hello\\World";
assert!(encoded == Header::encode_value(unencoded));
}
#[test]
fn decode_slash() {
let unencoded = r"Hello\World";
let encoded = r"Hello\\World";
assert!(unencoded == Header::decode_value(encoded));
}