add client::Builder to configure Clients
This commit is contained in:
		
							
								
								
									
										114
									
								
								src/client.rs
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								src/client.rs
									
									
									
									
									
								
							| @@ -4,21 +4,20 @@ use frame::Reason::*; | |||||||
| use proto::{self, Connection, WindowSize}; | use proto::{self, Connection, WindowSize}; | ||||||
|  |  | ||||||
| use bytes::{Bytes, IntoBuf}; | use bytes::{Bytes, IntoBuf}; | ||||||
| use futures::{AndThen, Async, AsyncSink, Future, MapErr, Poll, Sink}; | use futures::{Async, Future, MapErr, Poll}; | ||||||
| use http::{HeaderMap, Request, Response}; | use http::{HeaderMap, Request, Response}; | ||||||
| use tokio_io::{AsyncRead, AsyncWrite}; | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
| use tokio_io::io::WriteAll; | use tokio_io::io::WriteAll; | ||||||
|  |  | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::io::Error as IoError; | use std::io; | ||||||
|  | use std::marker::PhantomData; | ||||||
|  |  | ||||||
| /// In progress H2 connection binding | /// In progress H2 connection binding | ||||||
| pub struct Handshake<T: AsyncRead + AsyncWrite, B: IntoBuf = Bytes> { | pub struct Handshake<T: AsyncRead + AsyncWrite, B: IntoBuf = Bytes> { | ||||||
|     inner: AndThen< |     inner: MapErr<WriteAll<T, &'static [u8]>, fn(io::Error) -> ::Error>, | ||||||
|         MapErr<WriteAll<T, &'static [u8]>, fn(IoError) -> ::Error>, |     settings: Settings, | ||||||
|         Result<Client<T, B>, ::Error>, |     _marker: PhantomData<B>, | ||||||
|         fn((T, &'static [u8])) -> Result<Client<T, B>, ::Error>, |  | ||||||
|     >, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Marker type indicating a client peer | /// Marker type indicating a client peer | ||||||
| @@ -36,6 +35,12 @@ pub struct Body<B: IntoBuf> { | |||||||
|     inner: proto::StreamRef<B::Buf, Peer>, |     inner: proto::StreamRef<B::Buf, Peer>, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Build a Client. | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub struct Builder { | ||||||
|  |     settings: Settings, | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub(crate) struct Peer; | pub(crate) struct Peer; | ||||||
|  |  | ||||||
| @@ -43,16 +48,7 @@ impl<T> Client<T, Bytes> | |||||||
| where | where | ||||||
|     T: AsyncRead + AsyncWrite, |     T: AsyncRead + AsyncWrite, | ||||||
| { | { | ||||||
|     pub fn handshake(io: T) -> Handshake<T, Bytes> { |  | ||||||
|         Client::handshake2(io) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<T, B> Client<T, B> |  | ||||||
| where |  | ||||||
|     T: AsyncRead + AsyncWrite, |  | ||||||
|     B: IntoBuf, |  | ||||||
| { |  | ||||||
|     /// Bind an H2 client connection. |     /// Bind an H2 client connection. | ||||||
|     /// |     /// | ||||||
|     /// Returns a future which resolves to the connection value once the H2 |     /// Returns a future which resolves to the connection value once the H2 | ||||||
| @@ -60,41 +56,35 @@ where | |||||||
|     /// |     /// | ||||||
|     /// It's important to note that this does not **flush** the outbound |     /// It's important to note that this does not **flush** the outbound | ||||||
|     /// settings to the wire. |     /// settings to the wire. | ||||||
|     pub fn handshake2(io: T) -> Handshake<T, B> { |     pub fn handshake(io: T) -> Handshake<T, Bytes> { | ||||||
|  |         Builder::default().handshake(io) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Client<(), Bytes> { | ||||||
|  |     /// Creates a Client Builder to customize a Client before binding. | ||||||
|  |     pub fn builder() -> Builder { | ||||||
|  |         Builder::default() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T, B> Client<T, B> | ||||||
|  | where T: AsyncRead + AsyncWrite, | ||||||
|  |       B: IntoBuf | ||||||
|  | { | ||||||
|  |     fn handshake2(io: T, settings: Settings) -> Handshake<T, B> { | ||||||
|         use tokio_io::io; |         use tokio_io::io; | ||||||
|  |  | ||||||
|         debug!("binding client connection"); |         debug!("binding client connection"); | ||||||
|  |  | ||||||
|         let bind: fn((T, &'static [u8])) |  | ||||||
|             -> Result<Client<T, B>, ::Error> = |(io, _)| { |  | ||||||
|             debug!("client connection bound"); |  | ||||||
|  |  | ||||||
|             // Create the codec |  | ||||||
|             let mut codec = Codec::new(io); |  | ||||||
|  |  | ||||||
|             // Create the initial SETTINGS frame |  | ||||||
|             let settings = Settings::default(); |  | ||||||
|  |  | ||||||
|             // Send initial settings frame |  | ||||||
|             match codec.start_send(settings.into()) { |  | ||||||
|                 Ok(AsyncSink::Ready) => { |  | ||||||
|                     let connection = Connection::new(codec); |  | ||||||
|                     Ok(Client { |  | ||||||
|                         connection, |  | ||||||
|                     }) |  | ||||||
|                 }, |  | ||||||
|                 Ok(_) => unreachable!(), |  | ||||||
|                 Err(e) => Err(::Error::from(e)), |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         let msg: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; |         let msg: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; | ||||||
|         let handshake = io::write_all(io, msg) |         let handshake = io::write_all(io, msg) | ||||||
|             .map_err(::Error::from as fn(IoError) -> ::Error) |             .map_err(::Error::from as _); | ||||||
|             .and_then(bind); |  | ||||||
|  |  | ||||||
|         Handshake { |         Handshake { | ||||||
|             inner: handshake, |             inner: handshake, | ||||||
|  |             settings: settings, | ||||||
|  |             _marker: PhantomData, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -172,6 +162,30 @@ where | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ===== impl Builder ===== | ||||||
|  |  | ||||||
|  | impl Builder { | ||||||
|  |     /// Set the initial window size of the remote peer. | ||||||
|  |     pub fn initial_window_size(&mut self, size: u32) -> &mut Self { | ||||||
|  |         self.settings.set_initial_window_size(Some(size)); | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Bind an H2 client connection. | ||||||
|  |     /// | ||||||
|  |     /// Returns a future which resolves to the connection value once the H2 | ||||||
|  |     /// handshake has been completed. | ||||||
|  |     /// | ||||||
|  |     /// It's important to note that this does not **flush** the outbound | ||||||
|  |     /// settings to the wire. | ||||||
|  |     pub fn handshake<T, B>(&self, io: T) -> Handshake<T, B> | ||||||
|  |         where T: AsyncRead + AsyncWrite, | ||||||
|  |               B: IntoBuf | ||||||
|  |     { | ||||||
|  |         Client::handshake2(io, self.settings.clone()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| // ===== impl Handshake ===== | // ===== impl Handshake ===== | ||||||
|  |  | ||||||
| impl<T, B: IntoBuf> Future for Handshake<T, B> | impl<T, B: IntoBuf> Future for Handshake<T, B> | ||||||
| @@ -182,7 +196,21 @@ where | |||||||
|     type Error = ::Error; |     type Error = ::Error; | ||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { |     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||||
|         self.inner.poll() |         let (io, _) = try_ready!(self.inner.poll()); | ||||||
|  |  | ||||||
|  |         debug!("client connection bound"); | ||||||
|  |  | ||||||
|  |         // Create the codec | ||||||
|  |         let mut codec = Codec::new(io); | ||||||
|  |  | ||||||
|  |         // Send initial settings frame | ||||||
|  |         codec.buffer(self.settings.clone().into()) | ||||||
|  |            .expect("invalid SETTINGS frame"); | ||||||
|  |  | ||||||
|  |         let connection = Connection::new(codec, &self.settings); | ||||||
|  |         Ok(Async::Ready(Client { | ||||||
|  |             connection, | ||||||
|  |         })) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -66,6 +66,10 @@ impl Settings { | |||||||
|         self.initial_window_size |         self.initial_window_size | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub fn set_initial_window_size(&mut self, size: Option<u32>) { | ||||||
|  |         self.initial_window_size = size; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub fn max_concurrent_streams(&self) -> Option<u32> { |     pub fn max_concurrent_streams(&self) -> Option<u32> { | ||||||
|         self.max_concurrent_streams |         self.max_concurrent_streams | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -58,13 +58,15 @@ where | |||||||
|     P: Peer, |     P: Peer, | ||||||
|     B: IntoBuf, |     B: IntoBuf, | ||||||
| { | { | ||||||
|     pub fn new(codec: Codec<T, Prioritized<B::Buf>>) -> Connection<T, P, B> { |     pub fn new(codec: Codec<T, Prioritized<B::Buf>>, settings: &frame::Settings) -> Connection<T, P, B> { | ||||||
|         // TODO: Actually configure |         // TODO: Actually configure | ||||||
|         let streams = Streams::new(streams::Config { |         let streams = Streams::new(streams::Config { | ||||||
|             max_remote_initiated: None, |             max_remote_initiated: None, | ||||||
|             init_remote_window_sz: DEFAULT_INITIAL_WINDOW_SIZE, |             init_remote_window_sz: DEFAULT_INITIAL_WINDOW_SIZE, | ||||||
|             max_local_initiated: None, |             max_local_initiated: None, | ||||||
|             init_local_window_sz: DEFAULT_INITIAL_WINDOW_SIZE, |             init_local_window_sz: settings | ||||||
|  |                 .initial_window_size() | ||||||
|  |                 .unwrap_or(DEFAULT_INITIAL_WINDOW_SIZE), | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         Connection { |         Connection { | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| use codec::{Codec, RecvError}; | use codec::{Codec, RecvError}; | ||||||
| use frame::{self, Reason, StreamId}; | use frame::{self, Reason, Settings, StreamId}; | ||||||
| use frame::Reason::*; | use frame::Reason::*; | ||||||
| use proto::{self, Connection, WindowSize}; | use proto::{self, Connection, WindowSize}; | ||||||
|  |  | ||||||
| @@ -82,19 +82,18 @@ where | |||||||
|         let mut codec = Codec::new(io); |         let mut codec = Codec::new(io); | ||||||
|  |  | ||||||
|         // Create the initial SETTINGS frame |         // Create the initial SETTINGS frame | ||||||
|         let settings = frame::Settings::default(); |         let settings = Settings::default(); | ||||||
|  |  | ||||||
|         // Send initial settings frame |         // Send initial settings frame | ||||||
|         codec |         codec | ||||||
|             .buffer(settings.into()) |             .buffer(settings.into()) | ||||||
|             .ok() |  | ||||||
|             .expect("invalid SETTINGS frame"); |             .expect("invalid SETTINGS frame"); | ||||||
|  |  | ||||||
|         // Flush pending settings frame and then wait for the client preface |         // Flush pending settings frame and then wait for the client preface | ||||||
|         let handshake = Flush::new(codec) |         let handshake = Flush::new(codec) | ||||||
|             .and_then(ReadPreface::new) |             .and_then(ReadPreface::new) | ||||||
|             .map(move |codec| { |             .map(move |codec| { | ||||||
|                 let connection = Connection::new(codec); |                 let connection = Connection::new(codec, &Settings::default()); | ||||||
|                 Server { |                 Server { | ||||||
|                     connection, |                     connection, | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -62,7 +62,10 @@ fn send_recv_data() { | |||||||
|         ]) |         ]) | ||||||
|         .build(); |         .build(); | ||||||
|  |  | ||||||
|     let mut h2 = Client::handshake2(mock).wait().unwrap(); |     let mut h2 = Client::builder() | ||||||
|  |         .handshake(mock) | ||||||
|  |         .wait() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let request = Request::builder() |     let request = Request::builder() | ||||||
|         .method(Method::POST) |         .method(Method::POST) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user