| @@ -55,7 +55,7 @@ impl Request<Fresh> { | ||||
|         }; | ||||
|         debug!("port={}", port); | ||||
|  | ||||
|         let stream: S = try_io!(NetworkStream::connect(host.as_slice(), port)); | ||||
|         let stream: S = try_io!(NetworkStream::connect(host.as_slice(), port, url.scheme.as_slice())); | ||||
|         let stream = BufferedWriter::new(stream.abstract()); | ||||
|         let mut headers = Headers::new(); | ||||
|         headers.set(Host(host)); | ||||
|   | ||||
							
								
								
									
										20
									
								
								src/http.rs
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/http.rs
									
									
									
									
									
								
							| @@ -59,11 +59,13 @@ impl<R: Reader> Reader for HttpReader<R> { | ||||
|                     // None means we don't know the size of the next chunk | ||||
|                     None => try!(read_chunk_size(body)) | ||||
|                 }; | ||||
|                 debug!("Chunked read, remaining={}", rem); | ||||
|  | ||||
|                 if rem == 0 { | ||||
|                     // chunk of size 0 signals the end of the chunked stream | ||||
|                     // if the 0 digit was missing from the stream, it would | ||||
|                     // be an InvalidInput error instead. | ||||
|                     debug!("end of chunked"); | ||||
|                     return Err(io::standard_error(io::EndOfFile)); | ||||
|                 } | ||||
|  | ||||
| @@ -71,7 +73,12 @@ impl<R: Reader> Reader for HttpReader<R> { | ||||
|                 let count = try!(body.read(buf.mut_slice_to(to_read))); | ||||
|  | ||||
|                 rem -= count; | ||||
|                 *opt_remaining = if rem > 0 { Some(rem) } else { None }; | ||||
|                 *opt_remaining = if rem > 0 { | ||||
|                     Some(rem) | ||||
|                 } else { | ||||
|                     try!(eat(body, LINE_ENDING)); | ||||
|                     None | ||||
|                 }; | ||||
|                 Ok(count) | ||||
|             }, | ||||
|             EofReader(ref mut body) => { | ||||
| @@ -81,6 +88,16 @@ impl<R: Reader> Reader for HttpReader<R> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn eat<R: Reader>(rdr: &mut R, bytes: &[u8]) -> IoResult<()> { | ||||
|     for &b in bytes.iter() { | ||||
|         match try!(rdr.read_byte()) { | ||||
|             byte if byte == b => (), | ||||
|             _ => return Err(io::standard_error(io::InvalidInput)) | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| /// Chunked chunks start with 1*HEXDIGIT, indicating the size of the chunk. | ||||
| fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<uint> { | ||||
|     let mut size = 0u; | ||||
| @@ -112,6 +129,7 @@ fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<uint> { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     debug!("chunk size={}", size); | ||||
|     Ok(size) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| extern crate time; | ||||
| extern crate url; | ||||
| extern crate openssl; | ||||
| #[phase(plugin,link)] extern crate log; | ||||
| #[cfg(test)] extern crate test; | ||||
| extern crate "unsafe-any" as uany; | ||||
|   | ||||
							
								
								
									
										96
									
								
								src/net.rs
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								src/net.rs
									
									
									
									
									
								
							| @@ -1,7 +1,12 @@ | ||||
| //! A collection of traits abstracting over Listeners and Streams. | ||||
| use std::io::{IoResult, Stream, Listener, Acceptor}; | ||||
| use std::io::{IoResult, IoError, ConnectionAborted, InvalidInput, OtherIoError, | ||||
|               Stream, Listener, Acceptor}; | ||||
| use std::io::net::ip::{SocketAddr, Port}; | ||||
| use std::io::net::tcp::{TcpStream, TcpListener, TcpAcceptor}; | ||||
| use std::sync::{Arc, Mutex}; | ||||
|  | ||||
| use openssl::ssl::{SslStream, SslContext, Sslv23}; | ||||
| use openssl::ssl::error::{SslError, StreamError, OpenSslErrors, SslSessionClosed}; | ||||
|  | ||||
| /// The write-status indicating headers have not been written. | ||||
| pub struct Fresh; | ||||
| @@ -32,7 +37,7 @@ pub trait NetworkListener<S: NetworkStream, A: NetworkAcceptor<S>>: Listener<S, | ||||
|     fn socket_name(&mut self) -> IoResult<SocketAddr>; | ||||
| } | ||||
|  | ||||
| /// An abstraction to receive `HttpStream`s. | ||||
| /// An abstraction to receive `NetworkStream`s. | ||||
| pub trait NetworkAcceptor<S: NetworkStream>: Acceptor<S> + Clone + Send { | ||||
|     /// Closes the Acceptor, so no more incoming connections will be handled. | ||||
|     fn close(&mut self) -> IoResult<()>; | ||||
| @@ -44,7 +49,7 @@ pub trait NetworkStream: Stream + Clone + Send { | ||||
|     fn peer_name(&mut self) -> IoResult<SocketAddr>; | ||||
|  | ||||
|     /// Connect to a remote address. | ||||
|     fn connect(host: &str, port: Port) -> IoResult<Self>; | ||||
|     fn connect(host: &str, Port, scheme: &str) -> IoResult<Self>; | ||||
|  | ||||
|     /// Turn this into an appropriately typed trait object. | ||||
|     #[inline] | ||||
| @@ -113,9 +118,7 @@ pub struct HttpAcceptor { | ||||
| impl Acceptor<HttpStream> for HttpAcceptor { | ||||
|     #[inline] | ||||
|     fn accept(&mut self) -> IoResult<HttpStream> { | ||||
|         Ok(HttpStream { | ||||
|             inner: try!(self.inner.accept()) | ||||
|         }) | ||||
|         Ok(Http(try!(self.inner.accept()))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -128,39 +131,94 @@ impl NetworkAcceptor<HttpStream> for HttpAcceptor { | ||||
|  | ||||
| /// A wrapper around a TcpStream. | ||||
| #[deriving(Clone)] | ||||
| pub struct HttpStream { | ||||
|     inner: TcpStream | ||||
| pub enum HttpStream { | ||||
|     /// A stream over the HTTP protocol. | ||||
|     Http(TcpStream), | ||||
|     /// A stream over the HTTP protocol, protected by SSL. | ||||
|     // You may be asking wtf an Arc and Mutex? That's because SslStream | ||||
|     // doesn't implement Clone, and we need Clone to use the stream for | ||||
|     // both the Request and Response. | ||||
|     // FIXME: https://github.com/sfackler/rust-openssl/issues/6 | ||||
|     Https(Arc<Mutex<SslStream<TcpStream>>>, SocketAddr), | ||||
| } | ||||
|  | ||||
| impl Reader for HttpStream { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { | ||||
|         self.inner.read(buf) | ||||
|         match *self { | ||||
|             Http(ref mut inner) => inner.read(buf), | ||||
|             Https(ref mut inner, _) => inner.lock().read(buf) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Writer for HttpStream { | ||||
|     #[inline] | ||||
|     fn write(&mut self, msg: &[u8]) -> IoResult<()> { | ||||
|         self.inner.write(msg) | ||||
|         match *self { | ||||
|             Http(ref mut inner) => inner.write(msg), | ||||
|             Https(ref mut inner, _) => inner.lock().write(msg) | ||||
|         } | ||||
|     } | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> IoResult<()> { | ||||
|         self.inner.flush() | ||||
|         match *self { | ||||
|             Http(ref mut inner) => inner.flush(), | ||||
|             Https(ref mut inner, _) => inner.lock().flush(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| impl NetworkStream for HttpStream { | ||||
|     #[inline] | ||||
|     fn peer_name(&mut self) -> IoResult<SocketAddr> { | ||||
|         self.inner.peer_name() | ||||
|     fn connect(host: &str, port: Port, scheme: &str) -> IoResult<HttpStream> { | ||||
|         match scheme { | ||||
|             "http" => { | ||||
|                 debug!("http scheme"); | ||||
|                 Ok(Http(try!(TcpStream::connect(host, port)))) | ||||
|             }, | ||||
|             "https" => { | ||||
|                 debug!("https scheme"); | ||||
|                 let mut stream = try!(TcpStream::connect(host, port)); | ||||
|                 // we can't access the tcp stream once it's wrapped in an | ||||
|                 // SslStream, so grab the ip address now, just in case. | ||||
|                 let addr = try!(stream.peer_name()); | ||||
|                 let context = try!(SslContext::new(Sslv23).map_err(lift_ssl_error)); | ||||
|                 let stream = try!(SslStream::new(&context, stream).map_err(lift_ssl_error)); | ||||
|                 Ok(Https(Arc::new(Mutex::new(stream)), addr)) | ||||
|             }, | ||||
|             _ => { | ||||
|                 Err(IoError { | ||||
|                     kind: InvalidInput, | ||||
|                     desc: "Invalid scheme for Http", | ||||
|                     detail: None | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn connect(host: &str, port: Port) -> IoResult<HttpStream> { | ||||
|         Ok(HttpStream { | ||||
|             inner: try!(TcpStream::connect(host, port)) | ||||
|         }) | ||||
|     fn peer_name(&mut self) -> IoResult<SocketAddr> { | ||||
|         match *self { | ||||
|             Http(ref mut inner) => inner.peer_name(), | ||||
|             Https(_, addr) => Ok(addr) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn lift_ssl_error(ssl: SslError) -> IoError { | ||||
|     match ssl { | ||||
|         StreamError(err) => err, | ||||
|         SslSessionClosed => IoError { | ||||
|             kind: ConnectionAborted, | ||||
|             desc: "SSL Connection Closed", | ||||
|             detail: None | ||||
|         }, | ||||
|         // Unfortunately throw this away. No way to support this | ||||
|         // detail without a better Error abstraction. | ||||
|         OpenSslErrors(errs) => IoError { | ||||
|             kind: OtherIoError, | ||||
|             desc: "Error in OpenSSL", | ||||
|             detail: Some(format!("{}", errs)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user