Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
112 lines (97 sloc) 3.25 KB
use bytes::{BufMut, Bytes, BytesMut};
use futures;
use nom::{self, Needed};
use imap_proto;
use imap_proto::types::{Request, RequestId, Response};
use std::io;
use std::mem;
use tokio::net::TcpStream;
use tokio_codec::{Decoder, Encoder, Framed};
use tokio_tls::TlsStream;
pub struct ImapCodec {
decode_need_message_bytes: usize,
}
impl Default for ImapCodec {
fn default() -> Self {
Self {
decode_need_message_bytes: 0,
}
}
}
impl<'a> Decoder for ImapCodec {
type Item = ResponseData;
type Error = io::Error;
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, io::Error> {
if self.decode_need_message_bytes > buf.len() {
return Ok(None);
}
let (response, rsp_len) = match imap_proto::parse_response(buf) {
Ok((remaining, response)) => {
// This SHOULD be acceptable/safe: BytesMut storage memory is
// allocated on the heap and should not move. It will not be
// freed as long as we keep a reference alive, which we do
// by retaining a reference to the split buffer, below.
let response = unsafe { mem::transmute(response) };
(response, buf.len() - remaining.len())
}
Err(nom::Err::Incomplete(Needed::Size(min))) => {
self.decode_need_message_bytes = min;
return Ok(None);
}
Err(nom::Err::Incomplete(_)) => {
return Ok(None);
}
Err(err) => {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("{} during parsing of {:?}", err, buf),
));
}
};
let raw = buf.split_to(rsp_len).freeze();
self.decode_need_message_bytes = 0;
Ok(Some(ResponseData { raw, response }))
}
}
impl Encoder for ImapCodec {
type Item = Request;
type Error = io::Error;
fn encode(&mut self, msg: Self::Item, dst: &mut BytesMut) -> Result<(), io::Error> {
dst.put(msg.0.as_bytes());
dst.put(b' ');
dst.put(&msg.1);
dst.put("\r\n");
Ok(())
}
}
#[derive(Debug)]
pub struct ResponseData {
raw: Bytes,
// This reference is really scoped to the lifetime of the `raw`
// member, but unfortunately Rust does not allow that yet. It
// is transmuted to `'static` by the `Decoder`, instead, and
// references returned to callers of `ResponseData` are limited
// to the lifetime of the `ResponseData` struct.
//
// `raw` is never mutated during the lifetime of `ResponseData`,
// and `Response` does not not implement any specific drop glue.
response: Response<'static>,
}
impl ResponseData {
pub fn request_id(&self) -> Option<&RequestId> {
match self.response {
Response::Done { ref tag, .. } => Some(tag),
_ => None,
}
}
pub fn parsed<'a>(&'a self) -> &'a Response {
&self.response
}
}
pub type ImapTls = Framed<TlsStream<TcpStream>, ImapCodec>;
impl ImapTransport for ImapTls {}
pub trait ImapTransport:
futures::Stream<Item = ResponseData, Error = io::Error>
+ futures::Sink<SinkItem = Request, SinkError = io::Error>
{
}
You can’t perform that action at this time.