extern crate rotor; extern crate rotor_http; extern crate argparse; extern crate url; extern crate env_logger; use std::io::{stdout, stderr}; use std::io::Write; use std::net::ToSocketAddrs; use std::time::Duration; use std::process::exit; use url::{Url, Host}; use url::SchemeData::Relative; use argparse::{ArgumentParser, Store}; use rotor::{Scope, Time}; use rotor_http::client::{connect_tcp, Request, Head, Client, RecvMode}; use rotor_http::client::{Connection, Requester, Task, Version, ResponseError}; use rotor_http::client::{ProtocolError}; struct Context; struct Cli(Option); struct Req(Url); impl Client for Cli { type Requester = Req; type Seed = Url; fn create(seed: Self::Seed, _scope: &mut Scope<::Context>) -> Self { Cli(Some(seed)) } fn connection_idle(mut self, _conn: &Connection, scope: &mut Scope) -> Task { match self.0.take() { Some(url) => Task::Request(Cli(None), Req(url)), None => { scope.shutdown_loop(); Task::Close } } } fn connection_error(self, err: &ProtocolError, _scope: &mut Scope) { writeln!(&mut stderr(), "----- Bad response: {} -----", err).ok(); exit(1); } fn wakeup(self, _connection: &Connection, _scope: &mut Scope<::Context>) -> Task { unimplemented!(); } fn timeout(self, _connection: &Connection, _scope: &mut Scope<::Context>) -> Task { unimplemented!(); } } impl Requester for Req { type Context = Context; fn prepare_request(self, req: &mut Request, _scope: &mut Scope) -> Option { req.start("GET", &self.0.serialize_path().unwrap(), Version::Http11); req.add_header("Host", self.0.serialize_host().unwrap().as_bytes()).unwrap(); req.done_headers().unwrap(); req.done(); Some(self) } fn headers_received(self, head: Head, _request: &mut Request, scope: &mut Scope) -> Option<(Self, RecvMode, Time)> { println!("----- Headers -----"); println!("Status: {} {}", head.code, head.reason); for header in head.headers { println!("{}: {}", header.name, String::from_utf8_lossy(header.value)); } Some((self, RecvMode::Buffered(1 << 20), scope.now() + Duration::new(1000, 0))) } fn response_received(self, data: &[u8], _request: &mut Request, _scope: &mut Scope) { println!("----- Response -----"); stdout().write_all(data).unwrap(); if data.last() != Some(&b'\n') { println!(""); } } fn response_chunk(self, _chunk: &[u8], _request: &mut Request, _scope: &mut Scope) -> Option { unreachable!(); } fn response_end(self, _request: &mut Request, _scope: &mut Scope) { unreachable!(); } fn timeout(self, _request: &mut Request, _scope: &mut Scope) -> Option<(Self, Time)> { unreachable!(); } fn wakeup(self, _request: &mut Request, _scope: &mut Scope) -> Option { unimplemented!(); } fn bad_response(self, err: &ResponseError, _scope: &mut Scope) { writeln!(&mut stderr(), "----- Bad response: {} -----", err).ok(); exit(1); } } fn main() { env_logger::init().unwrap(); let mut url = Url::parse( "http://info.cern.ch/hypertext/WWW/TheProject.html").unwrap(); { let mut ap = ArgumentParser::new(); ap.refer(&mut url) .add_argument("url", Store, "Url to fetch"); ap.parse_args_or_exit(); } if &url.scheme != "http" { writeln!(&mut stderr(), "Only 'http://' urls are supported for now") .ok(); exit(1); } let addr = match url.scheme_data { Relative(ref scheme) => { // Can't implement as trait because of E0117 let port = scheme.port_or_default().unwrap(); match scheme.host { Host::Domain(ref d) => (d.as_str(), port).to_socket_addrs().unwrap().next().unwrap(), Host::Ipv4(ref a) => (*a, port).to_socket_addrs().unwrap().next().unwrap(), Host::Ipv6(ref a) => (*a, port).to_socket_addrs().unwrap().next().unwrap(), } } _ => unreachable!(), }; let creator = rotor::Loop::new(&rotor::Config::new()).unwrap(); let mut loop_inst = creator.instantiate(Context); loop_inst.add_machine_with(|scope| { connect_tcp::(scope, &addr, url) }).unwrap(); loop_inst.run().unwrap(); }