diff --git a/.gitignore b/.gitignore index 9fca7a3..9ebfdfd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,9 @@ build/ TAGS doc/http/ doc/src/http/ +doc/.lock +doc/*.js +doc/*.css lib/ .rust/ +Makefile diff --git a/.hgignore b/.hgignore index f44321b..30c769e 100644 --- a/.hgignore +++ b/.hgignore @@ -1,10 +1,15 @@ ~$ \.dSYM/ +\.swp$ ^src/http/generated/ ^bin/ ^build/ ^TAGS$ ^doc/http/ ^doc/src/http/ +^doc/\.lock$ +^doc/.*\.js$ +^doc/.*\.css$ ^lib/ ^.rust/ +^Makefile$ diff --git a/.travis.yml b/.travis.yml index eb104e9..f5e3b03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,16 @@ before_install: - sudo apt-get update install: - sudo apt-get install rust-nightly + - git clone https://github.com/sfackler/rust-openssl.git + - cd rust-openssl + - ./configure + - make + - cd .. + - mv rust-openssl ../ script: - - make check - - make docs + - WITHOUT_SSL=1 ./configure + - make all check + - ./configure + - make all check docs after_script: - curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh diff --git a/Makefile b/Makefile deleted file mode 100644 index f350cd6..0000000 --- a/Makefile +++ /dev/null @@ -1,80 +0,0 @@ -RUSTC ?= rustc -RUSTDOC ?= rustdoc -RUSTPKG ?= rustpkg -RUSTFLAGS ?= -O -RUST_REPOSITORY ?= ../rust -RUST_CTAGS ?= $(RUST_REPOSITORY)/src/etc/ctags.rust -VERSION=0.1-pre - -codegen_files=\ - src/codegen/branchify.rs \ - src/codegen/main.rs \ - src/codegen/read_method.rs \ - src/codegen/status.rs \ - -libhttp_so=build/libhttp-9296ff29-0.1-pre.so -http_files=\ - src/http/lib.rs \ - src/http/buffer.rs \ - src/http/common.rs \ - src/http/generated/read_method.rs \ - src/http/generated/status.rs \ - $(wildcard src/http/headers/*.rs) \ - $(wildcard src/http/client/*.rs) \ - $(wildcard src/http/server/*.rs) \ - src/http/memstream.rs \ - src/http/method.rs \ - src/http/rfc2616.rs - -http: $(libhttp_so) - -$(libhttp_so): $(http_files) - mkdir -p build/ - $(RUSTC) $(RUSTFLAGS) src/http/lib.rs --out-dir=build - -all: http examples docs - -build/codegen: $(codegen_files) - mkdir -p build/ - $(RUSTC) src/codegen/main.rs --out-dir=build - -src/http/generated: - mkdir -p src/http/generated - -src/http/generated/%.rs: build/codegen src/http/generated - build/codegen $(patsubst src/http/generated/%,%,$@) src/http/generated/ - -build/%:: src/%/main.rs $(libhttp_so) - mkdir -p "$(dir $@)" - $(RUSTC) $(RUSTFLAGS) $< -o $@ -L build/ - -examples: $(patsubst src/examples/%/main.rs,build/examples/%,$(wildcard src/examples/*/main.rs)) \ - $(patsubst src/examples/%/main.rs,build/examples/%,$(wildcard src/examples/*/*/main.rs)) - -docs: doc/http/index.html - -doc/http/index.html: $(http_files) - $(RUSTDOC) src/http/lib.rs - -build/tests: $(http_files) - $(RUSTC) $(RUSTFLAGS) --test -o build/tests src/http/lib.rs - -build/quicktests: $(http_files) - $(RUSTC) --test -o build/quicktests src/http/lib.rs - -# Can't wait for everything to build, optimised too? OK, you can save some time here. -quickcheck: build/quicktests - build/quicktests --test - -check: all build/tests - build/tests --test - -clean: - rm -rf src/http/generated/ src/http/codegen/codegen - rm -rf build/ - rm -rf bin/ .rust/ - -TAGS: - ctags -f TAGS --options=$(RUST_CTAGS) -R src - -.PHONY: all http examples docs clean check quickcheck diff --git a/Makefile.in b/Makefile.in index 37ac375..e15e050 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,42 +1,91 @@ -VPATH=%VPATH% - -RUST ?= rust +SSL_LIB ?= %SSL_LIB% +SSL_CFG ?= %SSL_CFG% +ifdef SSL_LIB + SSL_CFG += -L "$(SSL_LIB)" +endif RUSTC ?= rustc -RUSTFLAGS ?= -O -HOST_RUSTFLAGS ?= -O +RUSTDOC ?= rustdoc +RUSTPKG ?= rustpkg +RUSTFLAGS ?= -O $(SSL_CFG) +RUST_REPOSITORY ?= ../rust +RUST_CTAGS ?= $(RUST_REPOSITORY)/src/etc/ctags.rust VERSION=0.1-pre -libhttp_files=$(shell find $(VPATH)/src/http/ -type f -name '*.rs') \ - $(VPATH)/src/http/generated/read_method.rs \ - $(VPATH)/src/http/generated/status.rs +codegen_files=\ + src/codegen/branchify.rs \ + src/codegen/main.rs \ + src/codegen/read_method.rs \ + src/codegen/status.rs \ + +libhttp_so=build/.libhttp.timestamp +http_files=\ + $(wildcard src/http/*.rs) \ + src/http/generated/read_method.rs \ + src/http/generated/status.rs \ + $(wildcard src/http/headers/*.rs) \ + $(wildcard src/http/client/*.rs) \ + $(wildcard src/http/server/*.rs) + +http: $(libhttp_so) + +Makefile: configure Makefile.in + @echo "configure or Makefile.in changed, regenerating Makefile" + @DOING_RECONFIGURE=1 SSL_LIB="$(SSL_LIB)" SSL_CFG="$(SSL_CFG)" ./configure + @echo + @echo ====================== + @echo Please run make again! + @echo ====================== + @echo + @exit 1 + +$(libhttp_so): Makefile $(http_files) + mkdir -p build/ + $(RUSTC) $(RUSTFLAGS) src/http/lib.rs --out-dir=build + @touch build/.libhttp.timestamp + +all: http examples docs + +build/codegen: $(codegen_files) + mkdir -p build/ + $(RUSTC) src/codegen/main.rs --out-dir=build + +src/http/generated: + mkdir -p src/http/generated + +src/http/generated/%.rs: build/codegen src/http/generated + build/codegen $(patsubst src/http/generated/%,%,$@) src/http/generated/ + +build/%:: src/%/main.rs $(libhttp_so) + mkdir -p "$(dir $@)" + $(RUSTC) $(RUSTFLAGS) $< -o $@ -L build/ -all: libhttp.dummy +examples: $(patsubst src/examples/%/main.rs,build/examples/%,$(wildcard src/examples/*/main.rs)) \ + $(patsubst src/examples/%/main.rs,build/examples/%,$(wildcard src/examples/*/*/main.rs)) -codegen: $(wildcard $(VPATH)/src/codegen/*.rs) - $(RUSTC) $(HOST_RUSTFLAGS) $(VPATH)/src/codegen/main.rs -o codegen +docs: doc/http/index.html -$(VPATH)/src/http/generated: - mkdir -p $(VPATH)/src/http/generated +doc/http/index.html: $(http_files) + $(RUSTDOC) src/http/lib.rs -$(VPATH)/src/http/generated/%.rs: codegen $(VPATH)/src/http/generated - ./codegen $(patsubst $(VPATH)/src/http/generated/%,%,$@) $(VPATH)/src/http/generated/ +build/tests: $(http_files) + $(RUSTC) $(RUSTFLAGS) --test -o build/tests src/http/lib.rs -libhttp.dummy: $(libhttp_files) - $(RUSTC) $(RUSTFLAGS) $(VPATH)/src/http/lib.rs --out-dir . - touch $@ +build/quicktests: $(http_files) + $(RUSTC) --test -o build/quicktests src/http/lib.rs -check: tests - ./tests --test +# Can't wait for everything to build, optimised too? OK, you can save some time here. +quickcheck: build/quicktests + build/quicktests --test -tests: $(libhttp_files) - $(RUSTC) $(RUSTFLAGS) --test -o tests $(VPATH)/src/http/lib.rs +check: all build/tests + build/tests --test -clean-tests: - rm -f tests +clean: + rm -rf src/http/generated/ src/http/codegen/codegen + rm -rf build/ + rm -rf bin/ .rust/ -clean: clean-tests - rm -rf $(VPATH)src/http/generated/ codegen - rm -rf libhttp.dummy - rm -f *.so *.dylib *.rlib *.dll +TAGS: + ctags -f TAGS --options=$(RUST_CTAGS) -R src -.PHONY: all examples clean clean-tests +.PHONY: all http examples docs clean check quickcheck diff --git a/Makefile.servo.in b/Makefile.servo.in new file mode 100644 index 0000000..7b5685d --- /dev/null +++ b/Makefile.servo.in @@ -0,0 +1,49 @@ +VPATH=%VPATH% + +RUST ?= rust +RUSTC ?= rustc +RUSTFLAGS ?= -O +HOST_RUSTFLAGS ?= -O +VERSION=0.1-pre + +libhttp_files=$(shell find $(VPATH)/src/http/ -type f -name '*.rs') \ + $(VPATH)/src/http/generated/read_method.rs \ + $(VPATH)/src/http/generated/status.rs + +all: libhttp.dummy + +codegen: $(wildcard $(VPATH)/src/codegen/*.rs) + $(RUSTC) $(HOST_RUSTFLAGS) $(VPATH)/src/codegen/main.rs -o codegen + +$(VPATH)/src/http/generated: + mkdir -p $(VPATH)/src/http/generated + +$(VPATH)/src/http/generated/%.rs: codegen $(VPATH)/src/http/generated/ + ./codegen $(patsubst $(VPATH)/src/http/generated/%,%,$@) $(VPATH)/src/http/generated/ + +libhttp.dummy: $(libhttp_files) + $(RUSTC) $(RUSTFLAGS) $(VPATH)/src/http/lib.rs --out-dir . + touch $@ + +build/%:: src/%.rs libhttp.dummy + mkdir -p '$(dir $@)' + $(RUSTC) $(RUSTFLAGS) $< -o $@ -L build/ + +examples: build/examples/apache_fake build/examples/hello_world build/examples/info build/examples/client/client + +.PHONY: check +check: tests + +tests: $(libhttp_files) + $(RUSTC) $(RUSTFLAGS) --test -o tests $(VPATH)/src/http/lib.rs + ./tests --test + +clean-tests: + rm -f tests + +clean: clean-tests + rm -rf $(VPATH)src/http/generated/ codegen + rm -rf libhttp.dummy + rm -f *.so *.dylib *.dll + +.PHONY: all examples clean tests clean-tests diff --git a/README.rst b/README.rst index 42c92f6..6e4463f 100644 --- a/README.rst +++ b/README.rst @@ -68,6 +68,22 @@ At present, all of the example servers serve to http://127.0.0.1:8001/. Don't expect everything to work well. The server claims HTTP/1.1, but is not in any way compliant yet. +SSL support +----------- + +rust-http can be compiled with or without SSL support. + +To compile with SSL support, drop rust-openssl_ in a sibling directory of +rust-http (i.e. ``../rust-openssl`` from this file) and run its ``configure`` +and ``make``. rust-http's ``configure`` will then automatically detect it and +you will get SSL support enabled. + +To compile rust-http without SSL support, just don’t put rust-openssl_ where it +can find it. You'll then get an ``IoError { kind: InvalidInput, .. }`` if you +try to make an SSL request (e.g. HTTPS). + +.. _rust-openssl: https://github.com/sfackler/rust-openssl + Roadmap ------- diff --git a/comparisons/run.py b/comparisons/run.py index fa0fec4..cba94d8 100644 --- a/comparisons/run.py +++ b/comparisons/run.py @@ -144,6 +144,9 @@ def compile_server(self): '--opt-level=3', self.source, #'--out-dir', self.build_dir, '-L', '../build', # '../build/{}/http/'.format(RustServerRunner.HOST), + # Just in case it was built with openssl support. This should + # really be done better, based on the Makefile contents. + '-L', '../../rust-openssl/build', # Sorry, this main.rs business needs me to do this, or use rustpkg: '-o', os.path.join(self.build_dir, self.bin_name))).communicate() diff --git a/configure b/configure index 62a0f4c..df914e7 100644 --- a/configure +++ b/configure @@ -1,4 +1,4 @@ #!/bin/bash SRCDIR="$(cd $(dirname $0) && pwd)" -sed "s#%VPATH%#${SRCDIR}#" ${SRCDIR}/Makefile.in > Makefile +sed "s#%VPATH%#${SRCDIR}#" ${SRCDIR}/Makefile.servo.in > Makefile diff --git a/src/codegen/branchify.rs b/src/codegen/branchify.rs index 8d85241..27d6965 100644 --- a/src/codegen/branchify.rs +++ b/src/codegen/branchify.rs @@ -1,4 +1,4 @@ -#[macro_escape]; +#![macro_escape] use std::str::Chars; use std::io::IoResult; diff --git a/src/codegen/main.rs b/src/codegen/main.rs index 8faeb00..59433b0 100644 --- a/src/codegen/main.rs +++ b/src/codegen/main.rs @@ -1,6 +1,6 @@ -#[crate_id = "codegen"]; +#![crate_id = "codegen"] -#[feature(macro_rules)]; +#![feature(macro_rules)] extern crate collections; diff --git a/src/examples/client/main.rs b/src/examples/client/main.rs index 6b58a3d..29e7f37 100644 --- a/src/examples/client/main.rs +++ b/src/examples/client/main.rs @@ -1,4 +1,4 @@ -#[crate_id = "client"]; +#![crate_id = "client"] extern crate http; use http::client::RequestWriter; @@ -6,8 +6,7 @@ use http::method::Get; use http::headers::HeaderEnum; use std::os; use std::str; -use std::io::{Reader, println}; -use std::io::net::tcp::TcpStream; +use std::io::println; fn main() { format!("{}", Get); @@ -23,8 +22,8 @@ fn main() { } fn make_and_print_request(url: ~str) { - let request = RequestWriter::::new(Get, from_str(url).expect("Invalid URL :-(")) - .unwrap(); + let request: RequestWriter = RequestWriter::new(Get, from_str(url).expect("Invalid URL :-(")) + .unwrap(); println!("Request"); println!("======="); @@ -51,6 +50,9 @@ fn make_and_print_request(url: ~str) { println!(" - {}: {}", header.header_name(), header.header_value()); } println!("Body:"); - let body = response.read_to_end().unwrap(); - println(str::from_utf8(body).expect("Uh oh, response wasn't UTF-8")); + let body = match response.read_to_end() { + Ok(body) => body, + Err(err) => fail!("Reading response failed: {}", err), + }; + println(str::from_utf8(body.as_slice()).expect("Uh oh, response wasn't UTF-8")); } diff --git a/src/examples/server/apache_fake/main.rs b/src/examples/server/apache_fake/main.rs index f4dad3a..e36924b 100644 --- a/src/examples/server/apache_fake/main.rs +++ b/src/examples/server/apache_fake/main.rs @@ -2,13 +2,11 @@ //! configuration. Potentially useful for a smidgeon of performance comparison, though naturally //! Apache is doing a lot more than this does. -#[crate_id = "apache_fake"]; +#![crate_id = "apache_fake"] extern crate time; extern crate http; -use std::vec::Vec; - use std::io::net::ip::{SocketAddr, Ipv4Addr}; use std::io::Writer; diff --git a/src/examples/server/hello_world/main.rs b/src/examples/server/hello_world/main.rs index 3cdb66a..3034ce2 100644 --- a/src/examples/server/hello_world/main.rs +++ b/src/examples/server/hello_world/main.rs @@ -1,6 +1,6 @@ //! A very simple HTTP server which responds with the plain text "Hello, World!" to every request. -#[crate_id = "hello_world"]; +#![crate_id = "hello_world"] extern crate time; extern crate http; diff --git a/src/examples/server/info/main.rs b/src/examples/server/info/main.rs index d1b42be..e835642 100644 --- a/src/examples/server/info/main.rs +++ b/src/examples/server/info/main.rs @@ -1,7 +1,7 @@ //! A not-quite-trivial HTTP server which responds to requests by showing the request and response //! headers and any other information it has. -#[crate_id = "info"]; +#![crate_id = "info"] extern crate time; extern crate http; diff --git a/src/examples/server/request_uri/main.rs b/src/examples/server/request_uri/main.rs index 4963a47..12cd92d 100644 --- a/src/examples/server/request_uri/main.rs +++ b/src/examples/server/request_uri/main.rs @@ -4,13 +4,11 @@ //! This demonstrates some handling of the RequestURI, which has several possibilities and for which //! the correct values depend on the method. -#[crate_id = "request_uri"]; +#![crate_id = "request_uri"] extern crate time; extern crate http; -use std::vec::Vec; - use std::io::net::ip::{SocketAddr, Ipv4Addr}; use std::io::Writer; @@ -75,7 +73,7 @@ impl Server for RequestUriServer { }, AbsoluteUri(ref url) => { println!("absoluteURI, {}", url.to_str()); - //path = + //path = }, AbsolutePath(ref url) => { println!("absolute path, {}", url.to_owned()); diff --git a/src/http/buffer.rs b/src/http/buffer.rs index 039b19b..fb977ec 100644 --- a/src/http/buffer.rs +++ b/src/http/buffer.rs @@ -2,7 +2,6 @@ use std::io::{IoResult, Stream}; use std::cmp::min; -use std::vec::Vec; use std::slice; use std::num::ToStrRadix; @@ -12,16 +11,16 @@ static WRITE_BUF_SIZE: uint = 0x10000; // TODO: consider removing constants and giving a buffer size in the constructor pub struct BufferedStream { - wrapped: T, - read_buffer: Vec, + pub wrapped: T, + pub read_buffer: Vec, // The current position in the buffer - read_pos: uint, + pub read_pos: uint, // The last valid position in the reader - read_max: uint, - write_buffer: Vec, - write_len: uint, + pub read_max: uint, + pub write_buffer: Vec, + pub write_len: uint, - writing_chunked_body: bool, + pub writing_chunked_body: bool, } impl BufferedStream { diff --git a/src/http/client/mod.rs b/src/http/client/mod.rs index c3519fe..604dcd6 100644 --- a/src/http/client/mod.rs +++ b/src/http/client/mod.rs @@ -17,6 +17,8 @@ possible, but it's not elegant convenient yet. (Most notably, no transfer-encodi pub use self::request::RequestWriter; pub use self::response::ResponseReader; +pub use self::sslclients::NetworkStream; pub mod request; pub mod response; +mod sslclients; diff --git a/src/http/client/request.rs b/src/http/client/request.rs index 97a67ee..759e37f 100644 --- a/src/http/client/request.rs +++ b/src/http/client/request.rs @@ -35,6 +35,7 @@ let response = match request.read_response() { ``` */ + use url; use url::Url; use method::Method; @@ -65,13 +66,14 @@ use client::response::ResponseReader; } }*/ -pub struct RequestWriter { - // The place to write to (typically a TCP stream, io::net::tcp::TcpStream) - priv stream: Option>, - priv headers_written: bool, +pub struct RequestWriter { + // The place to write to (typically a network stream, which is + // io::net::tcp::TcpStream or an SSL wrapper around that) + stream: Option>, + headers_written: bool, /// The originating IP address of the request. - remote_addr: Option, + pub remote_addr: Option, /// The host name and IP address that the request was sent to; this must always be specified for /// HTTP/1.1 requests (or the request will be rejected), but for HTTP/1.0 requests the Host @@ -79,13 +81,16 @@ pub struct RequestWriter { //host: Host, // Now headers.host /// The headers sent with the request. - headers: ~HeaderCollection, + pub headers: ~HeaderCollection, /// The HTTP method for the request. - method: Method, + pub method: Method, /// The URL being requested. - url: Url, + pub url: Url, + + /// Should we use SSL? + use_ssl: bool, } /// Low-level HTTP request writing support @@ -94,9 +99,13 @@ pub struct RequestWriter { /// take place until writing is completed. /// /// At present, this only supports making one request per connection. -impl RequestWriter { +impl RequestWriter { /// Create a `RequestWriter` writing to the specified location pub fn new(method: Method, url: Url) -> IoResult> { + RequestWriter::new_request(method, url, false, true) + } + + pub fn new_request(method: Method, url: Url, use_ssl: bool, auto_detect_ssl: bool) -> IoResult> { let host = match url.port { None => Host { name: url.host.to_owned(), @@ -125,10 +134,12 @@ impl RequestWriter { // TODO: Error handling let addr = addr.unwrap(); - let port = url.port.clone().unwrap_or(~"80"); - let port = from_str(port); - // TODO: Error handling - let port = port.unwrap(); + // Default to 80, using the port specified or 443 if the protocol is HTTPS. + let port = match url.port { + Some(ref p) => from_str(*p).expect("You didn’t aught to give a bad port!"), + // FIXME: case insensitivity? + None => if url.scheme.as_slice() == "https" { 443 } else { 80 }, + }; Ok(SocketAddr { ip: addr, @@ -143,13 +154,20 @@ impl RequestWriter { headers: ~HeaderCollection::new(), method: method, url: url, + use_ssl: use_ssl, }; + + if auto_detect_ssl { + // FIXME: case insensitivity? + request.use_ssl = request.url.scheme.as_slice() == "https"; + } + request.headers.host = Some(host); Ok(request) } } -impl RequestWriter { +impl RequestWriter { /// Connect to the remote host if not already connected. pub fn try_connect(&mut self) -> IoResult<()> { @@ -169,7 +187,7 @@ impl RequestWriter { self.stream = match self.remote_addr { Some(addr) => { - let stream = try!(Connecter::connect(addr)); + let stream = try!(Connecter::connect(addr, self.url.host, self.use_ssl)); Some(BufferedStream::new(stream)) }, None => fail!("connect() called before remote_addr was set"), @@ -235,7 +253,7 @@ impl RequestWriter { } /// Write the request body. Note that any calls to `write()` will cause the headers to be sent. -impl Writer for RequestWriter { +impl Writer for RequestWriter { fn write(&mut self, buf: &[u8]) -> IoResult<()> { if !self.headers_written { try!(self.write_headers()); diff --git a/src/http/client/response.rs b/src/http/client/response.rs index 2ccd050..fe99636 100644 --- a/src/http/client/response.rs +++ b/src/http/client/response.rs @@ -10,19 +10,19 @@ use server::request::{RequestBuffer}; use headers::{EndOfFile, EndOfHeaders, MalformedHeaderSyntax, MalformedHeaderValue}; pub struct ResponseReader { - priv stream: BufferedStream, + stream: BufferedStream, /// The request which this is a response to - request: RequestWriter, + pub request: RequestWriter, /// The HTTP version number; typically `(1, 1)` or, less commonly, `(1, 0)`. - version: (uint, uint), + pub version: (uint, uint), /// The HTTP status indicated in the response. - status: Status, + pub status: Status, /// The headers received in the response. - headers: ~headers::response::HeaderCollection, + pub headers: ~headers::response::HeaderCollection, } fn bad_response_err() -> IoError { diff --git a/src/http/client/sslclients/mod.rs b/src/http/client/sslclients/mod.rs new file mode 100644 index 0000000..e967483 --- /dev/null +++ b/src/http/client/sslclients/mod.rs @@ -0,0 +1,16 @@ +//! SSL client support. +//! +//! Which particular library is used depends upon the configuration used at +//! compile time; at present it can only be OpenSSL (`--cfg openssl`); without +//! that, you won't be able to use SSL (an attempt to make an HTTPS connection +//! will return an error). + +#[cfg(openssl)] +pub use self::openssl::NetworkStream; +#[cfg(not(openssl))] +pub use self::none::NetworkStream; + +#[cfg(openssl)] +mod openssl; +#[cfg(not(openssl))] +mod none; diff --git a/src/http/client/sslclients/none.rs b/src/http/client/sslclients/none.rs new file mode 100644 index 0000000..4da088c --- /dev/null +++ b/src/http/client/sslclients/none.rs @@ -0,0 +1,53 @@ +//! No SSL support (neither OpenSSL nor NSS were compiled in). + +use std::io::net::ip::SocketAddr; +use std::io::net::tcp::TcpStream; +use std::io::{IoResult, IoError, InvalidInput}; +use connecter::Connecter; + +/// A TCP stream, plain text and with no SSL support. +/// +/// This build was made *without* SSL support; if you attempt to make an SSL +/// connection you will receive an `IoError` of the `InvalidInput` kind. +/// +/// (To build with SSL support, use ``--cfg openssl`` or ``--cfg nss``.) +pub enum NetworkStream { + priv NormalStream(TcpStream), +} + +impl Connecter for NetworkStream { + fn connect(addr: SocketAddr, _host: &str, use_ssl: bool) -> IoResult { + if use_ssl { + Err(IoError { + kind: InvalidInput, + desc: "http crate was compiled without SSL support", + detail: None, + }) + } else { + let stream = try!(TcpStream::connect(addr)); + Ok(NormalStream(stream)) + } + } +} + +impl Reader for NetworkStream { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + match *self { + NormalStream(ref mut ns) => ns.read(buf), + } + } +} + +impl Writer for NetworkStream { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + match *self { + NormalStream(ref mut ns) => ns.write(buf), + } + } + + fn flush(&mut self) -> IoResult<()> { + match *self { + NormalStream(ref mut ns) => ns.flush(), + } + } +} diff --git a/src/http/client/sslclients/openssl.rs b/src/http/client/sslclients/openssl.rs new file mode 100644 index 0000000..6d45d96 --- /dev/null +++ b/src/http/client/sslclients/openssl.rs @@ -0,0 +1,54 @@ +//! SSL support provided by OpenSSL. + +extern crate openssl; + +use std::io::net::ip::SocketAddr; +use std::io::net::tcp::TcpStream; +use std::io::IoResult; +use self::openssl::ssl::{SslStream, SslContext, Sslv23}; +use connecter::Connecter; + +/// A TCP stream, either plain text or SSL. +/// +/// This build was made with **OpenSSL** providing SSL support. +pub enum NetworkStream { + priv NormalStream(TcpStream), + priv SslProtectedStream(SslStream), +} + +impl Connecter for NetworkStream { + fn connect(addr: SocketAddr, _host: &str, use_ssl: bool) -> IoResult { + let stream = try!(TcpStream::connect(addr)); + if use_ssl { + let ssl_stream = SslStream::new(&SslContext::new(Sslv23), stream); + Ok(SslProtectedStream(ssl_stream)) + } else { + Ok(NormalStream(stream)) + } + } +} + +impl Reader for NetworkStream { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + match *self { + NormalStream(ref mut ns) => ns.read(buf), + SslProtectedStream(ref mut ns) => ns.read(buf), + } + } +} + +impl Writer for NetworkStream { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + match *self { + NormalStream(ref mut ns) => ns.write(buf), + SslProtectedStream(ref mut ns) => ns.write(buf), + } + } + + fn flush(&mut self) -> IoResult<()> { + match *self { + NormalStream(ref mut ns) => ns.flush(), + SslProtectedStream(ref mut ns) => ns.flush(), + } + } +} diff --git a/src/http/common.rs b/src/http/common.rs index 2b64a9c..3f281a2 100644 --- a/src/http/common.rs +++ b/src/http/common.rs @@ -172,7 +172,7 @@ macro_rules! test_reads { ($func:ident $($value:expr => $expected:expr),*) => {{ $( assert_eq!( - concat_idents!(read_, $func)(&mut MemReader::new($value.as_bytes().into_owned()), + concat_idents!(read_, $func)(&mut MemReader::new($value.bytes().collect::>()), |b| b == 0).ok(), $expected); )* diff --git a/src/http/connecter.rs b/src/http/connecter.rs index 3ba8504..78237fe 100644 --- a/src/http/connecter.rs +++ b/src/http/connecter.rs @@ -2,7 +2,6 @@ use std::io::IoResult; use std::io::net::ip::SocketAddr; -use std::io::net::tcp::TcpStream; /// A trait for the concept of opening a stream connected to a IP socket address. /// @@ -10,11 +9,5 @@ use std::io::net::tcp::TcpStream; /// connections in terms of *anything* that can make such a connection rather /// than in terms of `TcpStream` only. This is handy for testing and for SSL. pub trait Connecter { - fn connect(addr: SocketAddr) -> IoResult; -} - -impl Connecter for TcpStream { - fn connect(addr: SocketAddr) -> IoResult { - TcpStream::connect(addr) - } + fn connect(addr: SocketAddr, host: &str, use_ssl: bool) -> IoResult; } diff --git a/src/http/headers/accept_ranges.rs b/src/http/headers/accept_ranges.rs index a784e65..03e60b9 100644 --- a/src/http/headers/accept_ranges.rs +++ b/src/http/headers/accept_ranges.rs @@ -1,6 +1,5 @@ //! The Accept-Ranges request header, defined in RFC 2616, Section 14.5. -use std::vec::Vec; use std::io::IoResult; use std::ascii::StrAsciiExt; diff --git a/src/http/headers/connection.rs b/src/http/headers/connection.rs index 1d775ea..7e5ff85 100644 --- a/src/http/headers/connection.rs +++ b/src/http/headers/connection.rs @@ -58,7 +58,6 @@ impl super::HeaderConvertible for Connection { #[test] fn test_connection() { - use std::vec::Vec; use headers::test_utils::{assert_conversion_correct, assert_interpretation_correct, assert_invalid}; diff --git a/src/http/headers/content_type.rs b/src/http/headers/content_type.rs index 6dbdf7b..e264d3b 100644 --- a/src/http/headers/content_type.rs +++ b/src/http/headers/content_type.rs @@ -1,14 +1,13 @@ //! The Content-Type entity header, defined in RFC 2616, Section 14.17. use headers::serialization_utils::{push_parameters, WriterUtil}; -use std::vec::Vec; use std::io::IoResult; use std::fmt; #[deriving(Clone, Eq)] pub struct MediaType { - type_: ~str, - subtype: ~str, - parameters: Vec<(~str, ~str)>, + pub type_: ~str, + pub subtype: ~str, + pub parameters: Vec<(~str, ~str)>, } pub fn MediaType(type_: ~str, subtype: ~str, parameters: Vec<(~str, ~str)>) -> MediaType { diff --git a/src/http/headers/etag.rs b/src/http/headers/etag.rs index c39f066..ca8d97b 100644 --- a/src/http/headers/etag.rs +++ b/src/http/headers/etag.rs @@ -4,8 +4,8 @@ use std::fmt; #[deriving(Clone, Eq)] pub struct EntityTag { - weak: bool, - opaque_tag: ~str, + pub weak: bool, + pub opaque_tag: ~str, } pub fn weak_etag(opaque_tag: S) -> EntityTag { diff --git a/src/http/headers/host.rs b/src/http/headers/host.rs index 76f76ca..e558c42 100644 --- a/src/http/headers/host.rs +++ b/src/http/headers/host.rs @@ -8,12 +8,12 @@ use std::fmt; pub struct Host { /// The name of the host that was requested - name: ~str, + pub name: ~str, /// If unspecified, assume the default port was used (80 for HTTP, 443 for HTTPS). /// In that case, you shouldn't need to worry about it in URLs that you build, provided you /// include the scheme. - port: Option, + pub port: Option, } impl fmt::Show for Host { diff --git a/src/http/headers/mod.rs b/src/http/headers/mod.rs index e0160a0..11554b2 100644 --- a/src/http/headers/mod.rs +++ b/src/http/headers/mod.rs @@ -5,7 +5,6 @@ //! unknown headers are stored in a map in the traditional way. use url::Url; -use std::vec::Vec; use std::io::IoResult; use time::{Tm, strptime}; use rfc2616::{is_token_item, is_separator, CR, LF, SP, HT, COLON}; @@ -141,7 +140,7 @@ enum HeaderValueByteIteratorState { /// handled correctly so that nothing else needs to worry about it. Any linear whitespace (multiple /// spaces outside of a quoted-string) is compacted into a single SP. pub struct HeaderValueByteIterator<'a, R> { - reader: &'a mut R, + pub reader: &'a mut R, /// This field serves two purposes. *During* iteration, it will typically be ``None``, but /// certain cases will cause it to be a ``Some``, meaning that the next ``next()`` call will @@ -149,10 +148,10 @@ pub struct HeaderValueByteIterator<'a, R> { /// ``next()`` has returned ``None``), it will be the extra byte which it has had to consume /// from the stream because of the possibility of linear white space of the form ``CR LF SP``. /// It is guaranteed that if ``self.state == Finished`` this will be a ``Some``. - next_byte: Option, + pub next_byte: Option, - at_start: bool, - priv state: HeaderValueByteIteratorState, + pub at_start: bool, + state: HeaderValueByteIteratorState, } impl<'a, R: Reader> HeaderValueByteIterator<'a, R> { @@ -853,7 +852,7 @@ mod test { macro_rules! headers_mod { { - $attr:attr + #[$attr:meta] // Not using this because of a "local ambiguity" bug //$($attrs:attr)* pub mod $mod_name:ident; @@ -869,10 +868,9 @@ macro_rules! headers_mod { } => { pub mod $mod_name { //$($attrs;)* - $attr; + #[$attr] - #[allow(unused_imports)]; - use std::vec::Vec; + #[allow(unused_imports)] use std::io::IoResult; use time; use collections::treemap::{TreeMap, Entries}; @@ -886,8 +884,8 @@ macro_rules! headers_mod { #[deriving(Clone)] pub struct HeaderCollection { - $($lower_ident: Option<$htype>,)* - extensions: TreeMap<~str, ~str>, + $(pub $lower_ident: Option<$htype>,)* + pub extensions: TreeMap<~str, ~str>, } impl HeaderCollection { diff --git a/src/http/headers/serialization_utils.rs b/src/http/headers/serialization_utils.rs index 7c37181..d20c9aa 100644 --- a/src/http/headers/serialization_utils.rs +++ b/src/http/headers/serialization_utils.rs @@ -1,6 +1,5 @@ //! Utility functions for assisting with conversion of headers from and to the HTTP text form. -use std::vec::Vec; use std::slice; use std::ascii::Ascii; use std::io::IoResult; diff --git a/src/http/headers/test_utils.rs b/src/http/headers/test_utils.rs index f4c2bef..cac98c7 100644 --- a/src/http/headers/test_utils.rs +++ b/src/http/headers/test_utils.rs @@ -4,7 +4,7 @@ use std::fmt; use headers::{HeaderConvertible, HeaderValueByteIterator}; pub fn from_stream_with_str(s: &str) -> Option { - let mut bytes = s.as_bytes().into_owned(); + let mut bytes = s.bytes().collect::>(); bytes.push_all(bytes!("\r\n/")); let mut reader = MemReader::new(bytes); let mut iter = HeaderValueByteIterator::new(&mut reader); diff --git a/src/http/headers/transfer_encoding.rs b/src/http/headers/transfer_encoding.rs index 7bb3668..53a162a 100644 --- a/src/http/headers/transfer_encoding.rs +++ b/src/http/headers/transfer_encoding.rs @@ -2,7 +2,6 @@ //! //! Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding -use std::vec::Vec; use std::ascii::StrAsciiExt; use std::io::IoResult; use headers::serialization_utils::{WriterUtil, push_parameters}; diff --git a/src/http/lib.rs b/src/http/lib.rs index 5e4a5a8..74b32b6 100644 --- a/src/http/lib.rs +++ b/src/http/lib.rs @@ -1,18 +1,19 @@ -#[crate_id = "http#0.1-pre"]; +#![crate_id = "http#0.1-pre"] -#[comment = "Rust HTTP server"]; -#[license = "MIT/ASL2"]; -#[crate_type = "dylib"]; -#[crate_type = "rlib"]; +#![comment = "Rust HTTP server"] +#![license = "MIT/ASL2"] +#![crate_type = "dylib"] +#![crate_type = "rlib"] -#[doc(html_root_url = "http://www.rust-ci.org/chris-morgan/rust-http/doc/")]; +#![doc(html_root_url = "http://www.rust-ci.org/chris-morgan/rust-http/doc/")] -#[deny(non_camel_case_types)]; +#![deny(non_camel_case_types)] //#[deny(missing_doc)]; -#[feature(macro_rules)]; -#[feature(phase)]; -#[macro_escape]; +#![feature(default_type_params)] +#![feature(macro_rules)] +#![feature(phase)] +#![macro_escape] #[phase(syntax, link)] extern crate log; extern crate url; diff --git a/src/http/memstream.rs b/src/http/memstream.rs index f14b507..ff02a2b 100644 --- a/src/http/memstream.rs +++ b/src/http/memstream.rs @@ -44,7 +44,7 @@ impl Reader for MemWriterFakeStream { pub struct MemReaderFakeStream(MemReader); impl MemReaderFakeStream { - pub fn new(buf: ~[u8]) -> MemReaderFakeStream { MemReaderFakeStream(MemReader::new(buf)) } + pub fn new(buf: Vec) -> MemReaderFakeStream { MemReaderFakeStream(MemReader::new(buf)) } } impl Reader for MemReaderFakeStream { @@ -92,7 +92,7 @@ mod test { #[test] fn test_mem_reader_fake_stream() { - let mut reader = MemReaderFakeStream::new(~[0, 1, 2, 3, 4, 5, 6, 7]); + let mut reader = MemReaderFakeStream::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); let mut buf = ~[]; assert_eq!(reader.read(buf), Ok(0)); assert_eq!(reader.tell(), Ok(0)); diff --git a/src/http/server/mod.rs b/src/http/server/mod.rs index 410d71c..b721002 100644 --- a/src/http/server/mod.rs +++ b/src/http/server/mod.rs @@ -119,7 +119,7 @@ pub trait Server: Send + Clone { /// At present, only the IP address and port to bind to are needed, but it's possible that other /// options may turn up later. pub struct Config { - bind_address: SocketAddr, + pub bind_address: SocketAddr, } static PERF_DUMP_FREQUENCY : u64 = 10_000; diff --git a/src/http/server/request.rs b/src/http/server/request.rs index 8f14b22..b81e9ae 100644 --- a/src/http/server/request.rs +++ b/src/http/server/request.rs @@ -23,7 +23,7 @@ pub static MAX_METHOD_LEN: uint = 64; pub struct RequestBuffer<'a, S> { /// The socket connection to read from - stream: &'a mut BufferedStream, + pub stream: &'a mut BufferedStream, } impl<'a, S: Stream> RequestBuffer<'a, S> { @@ -167,7 +167,7 @@ fn test_read_request_line() { ($value:expr => $expected:expr) => {{ let expected = $expected; let mut stream = BufferedStream::new( - MemReaderFakeStream::new($value.as_bytes().to_owned())); + MemReaderFakeStream::new($value.bytes().collect::>())); assert_eq!(RequestBuffer::new(&mut stream).read_request_line(), expected); }} ) @@ -203,7 +203,7 @@ fn test_read_request_line() { /// An HTTP request sent to the server. pub struct Request { /// The originating IP address of the request. - remote_addr: Option, + pub remote_addr: Option, /// The host name and IP address that the request was sent to; this must always be specified for /// HTTP/1.1 requests (or the request will be rejected), but for HTTP/1.0 requests the Host @@ -211,24 +211,24 @@ pub struct Request { //host: Option, // Now in headers.host /// The headers sent with the request. - headers: ~headers::request::HeaderCollection, + pub headers: ~headers::request::HeaderCollection, /// The body of the request; empty for such methods as GET. - body: ~str, + pub body: ~str, /// The HTTP method for the request. - method: Method, + pub method: Method, /// The URI that was requested, as found in the Request-URI of the Request-Line. /// You will almost never need to use this; you should prefer the `url` field instead. - request_uri: RequestUri, + pub request_uri: RequestUri, /// Whether to close the TCP connection when the request has been served. /// The alternative is keeping the connection open and waiting for another request. - close_connection: bool, + pub close_connection: bool, /// The HTTP version number; typically `(1, 1)` or, less commonly, `(1, 0)`. - version: (uint, uint) + pub version: (uint, uint) } /// The URI (Request-URI in RFC 2616) as specified in the Status-Line of an HTTP request @@ -373,7 +373,7 @@ impl Request { match request.headers.content_length { Some(length) => { match buffer.read_exact(length) { - Ok(body) => match str::from_utf8(body) { + Ok(body) => match str::from_utf8(body.as_slice()) { Some(body_str) => request.body = body_str.to_owned(), None => return (request, Err(status::BadRequest)) }, diff --git a/src/http/server/response.rs b/src/http/server/response.rs index 4ca9ee7..c756ac8 100644 --- a/src/http/server/response.rs +++ b/src/http/server/response.rs @@ -20,11 +20,11 @@ use headers::transfer_encoding::Chunked; pub struct ResponseWriter<'a> { // The place to write to (typically a TCP stream, io::net::tcp::TcpStream) - priv writer: &'a mut BufferedStream, - priv headers_written: bool, - request: &'a Request, - headers: ~HeaderCollection, - status: status::Status, + writer: &'a mut BufferedStream, + headers_written: bool, + pub request: &'a Request, + pub headers: ~HeaderCollection, + pub status: status::Status, } impl<'a> ResponseWriter<'a> {