1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::{collections::HashMap, hash::Hash};

use crate::{errors::*, properties::Properties};
use crate::transport::Transport;
use crate::tcp::TcpTransport;
use crate::ssl::SslTransport;
use crate::protocol::{MessageType, ReplyData, RequestData, Identity, Encapsulation};
use crate::encoding::FromBytes;
use pest::Parser;
use std::rc::Rc;
use std::cell::RefCell;


#[derive(Parser)]
#[grammar = "proxystring.pest"]
pub struct ProxyParser;


pub struct Proxy {
    pub transport: Rc<RefCell<dyn Transport + 'static>>,
    pub request_id: i32,
    pub ident: String,
    pub host: String,
    pub port: i32,
    pub context: Option<HashMap<String, String>>
}

impl Proxy {
    pub fn new(proxy_string: &str, properties: &Properties) -> Result<Proxy, Box<dyn std::error::Error>> { 
        let mut ident = "";
        let mut protocol = "";
        let mut host = "";
        let mut port = "";

        let result = ProxyParser::parse(Rule::proxystring, proxy_string)?.next().unwrap();
        for pair in result.into_inner() {
            match pair.as_rule() {
                Rule::ident => {
                    ident = pair.as_str();
                }
                Rule::endpoint => {
                    for child in pair.into_inner() {                        
                        match child.as_rule() {
                            Rule::endpoint_protocol => {
                                protocol = child.as_str();
                            }
                            Rule::endpoint_host | Rule::endpoint_port => {
                                for item in child.into_inner() {
                                    match item.as_rule() {
                                        Rule::hostname | Rule::ip => {
                                            host = item.as_str();
                                        }
                                        Rule::port => {
                                            port = item.as_str();
                                        }
                                        _ => return Err(Box::new(ParsingError::new(&format!("Unexpected proxy string rule: {:?}", item.as_rule()))))
                                    };
                                }
                            }
                            _ => return Err(Box::new(ParsingError::new(&format!("Unexpected proxy string rule: {:?}", child.as_rule()))))
                        };
                    }
                }
                Rule::EOI => {}
                _ => return Err(Box::new(ParsingError::new("Unexpected rule while parsing proxy string.")))
            };
        }

        let address = &(host.to_owned() + ":" + port);
        let transport: Rc<RefCell<dyn Transport>> = match protocol {
            "default" | "tcp" => Rc::new(RefCell::new(TcpTransport::new(address)?)),
            "ssl" => Rc::new(RefCell::new(SslTransport::new(address, properties)?)),
            _ => return Err(Box::new(ProtocolError::new(&format!("Unsupported protocol: {}", protocol))))
        };
        Ok(Proxy {
            transport: transport,
            request_id: 0,
            ident: String::from(ident),
            host: String::from(host),
            port: port.parse()?,
            context: None
        })
    }

    pub fn ice_context(&mut self, context: HashMap<String, String>) -> Proxy {
        Proxy {
            transport: self.transport.clone(),
            request_id: self.request_id,
            ident: self.ident.clone(),
            host: self.host.clone(),
            port: self.port,
            context: Some(context)
        }
    }

    pub fn create_request(&mut self, identity_name: &str, operation: &str, mode: u8, params: &Encapsulation, context: Option<HashMap<String, String>>) -> RequestData {
        let context = match context {
            Some(context) => context,
            None => {
                match self.context.as_ref() {
                    Some(context) => context.clone(),
                    None => HashMap::new()
                }
            }
        };
        self.request_id = self.request_id + 1;
        RequestData {
            request_id: self.request_id,
            id: Identity::new(identity_name),
            facet: Vec::new(),
            operation: String::from(operation),
            mode: mode,
            context: context,
            params: params.clone()
        }
    }

    pub fn make_request<T: 'static + std::fmt::Debug + std::fmt::Display + FromBytes>(&mut self, request: &RequestData) -> Result<ReplyData, Box<dyn std::error::Error>>
    {
        let mut tx = self.transport.borrow_mut();
        tx.make_request(request)?;
        let reply = tx.read_message()?;
        match reply {
            MessageType::Reply(_header, reply) => {
                match reply.status {
                    1 => {
                        let mut read = 0;
                        Err(Box::new(UserError {
                            exception: T::from_bytes(&reply.body.data, &mut read)?
                        }))
                    }
                    _ => Ok(reply)
                }
            },
            _ => Err(Box::new(ProtocolError::new(&format!("Unsupported message type: {:?}", reply))))
        }
    }
}