Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Api module for libhttp's client #14

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 50 additions & 11 deletions src/examples/client/main.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,90 @@
extern mod http;
use http::client::RequestWriter;
use http::client::ResponseReader;
use http::method::Get;
use http::headers::HeaderEnum;
use std::os;
use std::str;
use std::io::Reader;
use std::io::net::tcp::TcpStream;

// API Examples
use http::client::api::{get, RequestArgs};

fn main() {
format!("{}", Get);
let args = os::args();
match args.len() {
0 => unreachable!(),
2 => make_and_print_request(args[1]),
2 => get_example(args[1]),
_ => {
println!("Usage: {} URL", args[0]);
return;
},
};
}

fn make_and_print_request(url: ~str) {
let request = RequestWriter::new(Get, from_str(url).expect("Invalid URL :-("));

// NOTE(flaper87): Consider moving this to the
// request sender and print it if built in debug
// mode.
fn debug_request(request: &RequestWriter<TcpStream>) {
println("Request");
println("=======");
println("");
println!("URL: {}", request.url.to_str());
println!("Remote address: {:?}", request.remote_addr);
println!("Method: {}", request.method);
println("Headers:");
for header in request.headers.iter() {
println!(" - {}: {}", header.header_name(), header.header_value());
}
}

// NOTE(flaper87): Consider moving this to the
// request sender and print it if built in debug
// mode.
fn debug_response(response: &ResponseReader<TcpStream>) {
println("");
println("Response");
println("========");
println("");
let mut response = match request.read_response() {
Ok(response) => response,
Err(_request) => fail!("This example can progress no further with no response :-("),
};
println!("Status: {}", response.status);
println("Headers:");
for header in response.headers.iter() {
println!(" - {}: {}", header.header_name(), header.header_value());
}
}

fn get_example(url: ~str) {
let params = ~[(~"test", ~"value")];
let args = RequestArgs{params: Some(params), headers: None, data: None};
let response = get(url, Some(args));

let mut response = match response {
Ok(response) => response,
Err(_request) => fail!("This example can progress no further with no response :-("),
};

debug_response(&response);

print("\n");
println("Response:");
let body = response.read_to_end();
println(str::from_utf8_slice(body));
}

fn make_and_print_request(url: ~str) {
let request = RequestWriter::new(Get, from_str(url).expect("Invalid URL :-("));

debug_request(&request);
for header in request.headers.iter() {
println!(" - {}: {}", header.header_name(), header.header_value());
}

let mut response = match request.read_response() {
Ok(response) => response,
Err(_request) => fail!("This example can progress no further with no response :-("),
};

debug_response(&response);

println("Body:");
let body = response.read_to_end();
println(str::from_utf8_slice(body));
Expand Down
94 changes: 94 additions & 0 deletions src/http/client/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use std::default::Default;
use std::io::net::tcp::TcpStream;

use method;
use headers::request::HeaderCollection;

use client::request::RequestWriter;
use client::response::ResponseReader;
use extra::url::{Url, Query};

pub struct RequestArgs {

// Request data
data: Option<~[u8]>,

// Query Parameters
params: Option<Query>,

// Request Headers
headers: Option<~HeaderCollection>,
}

impl Default for RequestArgs {

fn default() -> RequestArgs {
RequestArgs{data: None, params: None, headers: None}
}
}

// Need a fix for https://github.com/mozilla/rust/issues/9056
// before we can use this.
//pub static DEFAULT_ARGS: RequestArgs = RequestArgs{params: None,
// headers: None};

// TODO: Implement a Response trait

pub fn request(method: method::Method, url: ~str, args: Option<RequestArgs>)
-> Result<ResponseReader<TcpStream>, RequestWriter<TcpStream>>{

let default = args.unwrap_or_default();

// Push all query params to the URL.
let mut url: Url = FromStr::from_str(url).expect("Uh oh, that's *really* badly broken!");

if default.params.is_some() {
url.query.push_all(*(default.params.get_ref()));
}

// At this point, we're ready to finally send
// the request. First thing is to write headers,
// then the request data and later get the response
// from the server.
let mut request = RequestWriter::new(method, url);

// Write data if there's some
if default.data.is_some() {
request.send(*(default.data.get_ref()));
}

// This will flush the request's
// stream and get the response from
// the server.
request.read_response()
}

pub fn get(url: ~str, args: Option<RequestArgs>)
-> Result<ResponseReader<TcpStream>, RequestWriter<TcpStream>> {

request(method::Get, url, args)
}

pub fn post(url: ~str, args: Option<RequestArgs>)
-> Result<ResponseReader<TcpStream>, RequestWriter<TcpStream>> {

request(method::Post, url, args)
}

pub fn patch(url: ~str, args: Option<RequestArgs>)
-> Result<ResponseReader<TcpStream>, RequestWriter<TcpStream>> {

request(method::Patch, url, args)
}

pub fn put(url: ~str, args: Option<RequestArgs>)
-> Result<ResponseReader<TcpStream>, RequestWriter<TcpStream>> {

request(method::Put, url, args)
}

pub fn delete(url: ~str, args: Option<RequestArgs>)
-> Result<ResponseReader<TcpStream>, RequestWriter<TcpStream>> {

request(method::Delete, url, args)
}
1 change: 1 addition & 0 deletions src/http/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ possible, but it's not elegant convenient yet. (Most notably, no transfer-encodi
pub use self::request::RequestWriter;
pub use self::response::ResponseReader;

pub mod api;
pub mod request;
pub mod response;
35 changes: 32 additions & 3 deletions src/http/client/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ impl RequestWriter<TcpStream> {
if self.url.query.len() > 0 { "?" } else { "" },
url::query_to_str(&self.url.query));

// `write_all` adds '\r\n' at the end, no
// need to terminate the headers section
// here.
self.headers.write_all(&mut self.stream);
self.headers_written = true;
}
Expand All @@ -224,14 +227,40 @@ impl RequestWriter<TcpStream> {
None => Err(self), // TODO: raise condition
}
}

/**
* Send data to the remote server.
* This method appends Content-Length
* to headers and sends them. If headers
* where already sent, it will send data
* without the Content-Length.
* TODO: Implement chunked request, perhaps
* in a `send_chunked` method.
*/
pub fn send(&mut self, buf: &[u8]) {

// NOTE: Should we make this fail?
// If 'Content-Length' is not sent
// some servers won't read the request
// body.
if !self.headers_written {
self.headers.content_length = Some(buf.len());
self.write_headers();
}
self.write(buf);
}
}

/// Write the request body. Note that any calls to `write()` will cause the headers to be sent.
impl Writer for RequestWriter<TcpStream> {
fn write(&mut self, buf: &[u8]) {
if (!self.headers_written) {
self.write_headers();
}
// No data must be sent before
// sending headers. Let's make
// sure that's the case.
self.try_write_headers();

// Now we're good to send
// some data.
self.stream.write(buf);
}

Expand Down
4 changes: 4 additions & 0 deletions src/http/client/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ impl<S: Stream> ResponseReader<S> {
headers: headers,
})
}

pub fn get_content(&mut self) -> ~[u8] {
self.stream.read_to_end()
}
}

impl<S: Stream> Reader for ResponseReader<S> {
Expand Down