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
use futures::{Async, Future}; use tk_bufstream::{ReadBuf, WriteBuf}; use super::{Error, Encoder, EncoderDone, Head}; use super::RecvMode; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum BodyKind { Fixed(u64), Chunked, Unsupported, } /// This is a low-level interface to the http server pub trait Dispatcher<S> { /// The codec type for this dispatcher /// /// In many cases the type is just `Box<Codec<S>>`, but it left as /// associated type make different types of middleware cheaper. type Codec: Codec<S>; /// Received headers of a request /// /// At this point we already extracted all the headers and other data /// that we need to ensure correctness of the protocol. If you need /// to handle some data from the headers you need to store them somewhere /// (for example on `self`) for further processing. fn headers_received(&mut self, headers: &Head) -> Result<Self::Codec, Error>; } /// The type represents a consumer of a single request and yields a writer of /// a response (the latter is a ``ResponseFuture`` pub trait Codec<S> { /// This is a future returned by `start_response` /// /// It's fine if it's just `Box<Future<Item=EncoderDone<S>, Error>>` in /// most cases. type ResponseFuture: Future<Item=EncoderDone<S>, Error=Error>; /// Return a mode which will be used to receive request body /// /// /// Note: this mode not only influences the size of chunks that /// `data_received` recieves and amount of buffering, but also it /// constrains the sequence between calls of `start_response()` /// and `data_received()`. /// /// Called once, right after `headers_received` fn recv_mode(&mut self) -> RecvMode; /// Chunk of the response body received /// /// `end` equals to `true` for the last chunk of the data. /// /// Method returns `Async::Ready(x)` to denote that it has consumed `x` /// bytes. If there are some bytes left in the buffer they will be passed /// again on the call. /// /// If the response is empty, or last chunk arrives later and it's empty /// we call `c.data_received(b"", true)` on every wakeup, /// until `Async::Ready(0)` is returned (this helps to drive future that /// might complete on response completion without spawning another ones, /// but note that next response can't start writing in the meantime). /// /// Protocol panics if returned number of bytes larger than `data.len()`. /// fn data_received(&mut self, data: &[u8], end: bool) -> Result<Async<usize>, Error>; /// Start writing a response /// /// This method is called when there all preceding requests are either /// send to the network or already buffered. It can be called before /// `data_received()` but not before `headers_received()` (that would not /// make sense). /// /// Everything you write into a buffer might be flushed to the network /// immediately (or as fast as you yield to main loop). On the other /// hand we might buffer/pipeline multiple responses at once. fn start_response(&mut self, e: Encoder<S>) -> Self::ResponseFuture; /// Called after future retunrted by `start_response` done if recv mode /// is `Hijack` /// /// Note: both input and output buffers can contain some data. fn hijack(&mut self, _output: WriteBuf<S>, _input: ReadBuf<S>) { panic!("`Codec::recv_mode` returned `Hijack` but \ no hijack() method implemented"); } } impl<S, F> Codec<S> for Box<Codec<S, ResponseFuture=F>> where F: Future<Item=EncoderDone<S>, Error=Error>, { type ResponseFuture = F; fn recv_mode(&mut self) -> RecvMode { (**self).recv_mode() } fn data_received(&mut self, data: &[u8], end: bool) -> Result<Async<usize>, Error> { (**self).data_received(data, end) } fn start_response(&mut self, e: Encoder<S>) -> Self::ResponseFuture { (**self).start_response(e) } fn hijack(&mut self, output: WriteBuf<S>, input: ReadBuf<S>) { (**self).hijack(output, input) } }