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
139
use std::ops::Deref;
use std::io::{self, Read, Write};

use hyper::{self, Method, Request, HttpVersion, Uri, Headers, Body, Chunk};
use tokio_core::reactor::Handle;
use tokio_io::AsyncRead;
use futures::{Async, Stream};

/// `Context` represents the context of the current HTTP request.
///
/// A `Context` consists of:
///     - A [`Handle`] referencing the event loop in which this request is being
///       handled.
///     - The current HTTP [`Request`].
///
/// [`Handle`]: https://docs.rs/tokio-core/0.1/tokio_core/reactor/struct.Handle.html
/// [`Request`]: http://doc.rust-lang.org/hyper/0.11/hyper/client/struct.Request.html
pub struct Context {
    method: Method,
    uri: Uri,
    version: HttpVersion,
    headers: Headers,
    handle: Handle,
    body: Body,
    chunk: Option<(Chunk, usize)>,
}

impl Context {
    pub(crate) fn new(request: Request, handle: Handle) -> Self {
        let (method, uri, version, headers, body) = request.deconstruct();

        Context { handle,
            method,
            uri,
            version,
            headers,
            body,
            chunk: None
        }
    }

    /// Return a reference to a handle to the event loop this `Context` is associated with.
    #[inline]
    pub fn handle(&self) -> &Handle {
        &self.handle
    }

    /// Returns a reference to the request HTTP version.
    #[inline]
    pub fn version(&self) -> &HttpVersion {
        &self.version
    }

    /// Returns a reference to the request headers.
    #[inline]
    pub fn headers(&self) -> &Headers {
        &self.headers
    }

    /// Returns a reference to the request HTTP method.
    #[inline]
    pub fn method(&self) -> &Method {
        &self.method
    }

    /// Returns a reference to the request URI.
    #[inline]
    pub fn uri(&self) -> &Uri {
        &self.uri
    }

    /// Returns a reference to the request path.
    #[inline]
    pub fn path(&self) -> &str {
        self.uri.path()
    }

    /// Returns a reference to the request body.
    #[inline]
    pub fn body(&self) -> &Body {
        &self.body
    }
}

impl Deref for Context {
    type Target = Handle;

    fn deref(&self) -> &Self::Target {
        &self.handle
    }
}

impl Read for Context {
    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
        if let Some((chunk, index)) = self.chunk.take() {
            let written = buf.write(&chunk[index..])?;

            if index + written < chunk.len() {
                self.chunk = Some((chunk, index + written));
            } else {
                self.chunk = None;
            }

            return Ok(written);
        }

        match self.body.poll() {
            Ok(Async::Ready(chunk)) => {
                Ok(match chunk {
                    Some(chunk) => {
                        let written = buf.write(&chunk)?;

                        if written < chunk.len() {
                            self.chunk = Some((chunk, written));
                        }

                        written
                    }

                    None => {
                        0
                    }
                })
            }

            Ok(Async::NotReady) => Err(io::ErrorKind::WouldBlock.into()),
            Err(error) => {
                match error {
                    hyper::Error::Io(error) => Err(error),
                    _ => {
                        Err(io::Error::new(io::ErrorKind::Other, Box::new(error)))
                    }
                }
            }
        }
    }
}

impl AsyncRead for Context { }