Split common headers into a submodule and into their own files
This is a more extensible place to put them and doesn't clutter up header/mod.rs as much as the old scheme did. Fixes #8
This commit is contained in:
44
src/header/common/accept.rs
Normal file
44
src/header/common/accept.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use header::Header;
|
||||
use std::fmt::{mod, Show};
|
||||
use mime::Mime;
|
||||
|
||||
/// The `Accept` header.
|
||||
///
|
||||
/// The `Accept` header is used to tell a server which content-types the client
|
||||
/// is capable of using. It can be a comma-separated list of `Mime`s, and the
|
||||
/// priority can be indicated with a `q` parameter.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// # use hyper::header::Headers;
|
||||
/// # use hyper::header::common::Accept;
|
||||
/// use hyper::mime::{Mime, Text, Html, Xml};
|
||||
/// # let mut headers = Headers::new();
|
||||
/// headers.set(Accept(vec![ Mime(Text, Html, vec![]), Mime(Text, Xml, vec![]) ]));
|
||||
/// ```
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct Accept(pub Vec<Mime>);
|
||||
|
||||
impl Header for Accept {
|
||||
fn header_name(_: Option<Accept>) -> &'static str {
|
||||
"accept"
|
||||
}
|
||||
|
||||
fn parse_header(_raw: &[Vec<u8>]) -> Option<Accept> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Accept(ref value) = *self;
|
||||
let last = value.len() - 1;
|
||||
for (i, mime) in value.iter().enumerate() {
|
||||
try!(mime.fmt(fmt));
|
||||
if i < last {
|
||||
try!(", ".fmt(fmt));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
45
src/header/common/connection.rs
Normal file
45
src/header/common/connection.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use header::Header;
|
||||
use std::fmt::{mod, Show};
|
||||
use super::from_one_raw_str;
|
||||
use std::from_str::FromStr;
|
||||
|
||||
/// The `Connection` header.
|
||||
///
|
||||
/// Describes whether the socket connection should be closed or reused after
|
||||
/// this request/response is completed.
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub enum Connection {
|
||||
/// The `keep-alive` connection value.
|
||||
KeepAlive,
|
||||
/// The `close` connection value.
|
||||
Close
|
||||
}
|
||||
|
||||
impl FromStr for Connection {
|
||||
fn from_str(s: &str) -> Option<Connection> {
|
||||
debug!("Connection::from_str =? {}", s);
|
||||
match s {
|
||||
"keep-alive" => Some(KeepAlive),
|
||||
"close" => Some(Close),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Header for Connection {
|
||||
fn header_name(_: Option<Connection>) -> &'static str {
|
||||
"connection"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<Connection> {
|
||||
from_one_raw_str(raw)
|
||||
}
|
||||
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
KeepAlive => "keep-alive",
|
||||
Close => "close",
|
||||
}.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
25
src/header/common/content_length.rs
Normal file
25
src/header/common/content_length.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use header::Header;
|
||||
use std::fmt::{mod, Show};
|
||||
use super::from_one_raw_str;
|
||||
|
||||
/// The `Content-Length` header.
|
||||
///
|
||||
/// Simply a wrapper around a `uint`.
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct ContentLength(pub uint);
|
||||
|
||||
impl Header for ContentLength {
|
||||
fn header_name(_: Option<ContentLength>) -> &'static str {
|
||||
"content-length"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<ContentLength> {
|
||||
from_one_raw_str(raw).map(|u| ContentLength(u))
|
||||
}
|
||||
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let ContentLength(ref value) = *self;
|
||||
value.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
27
src/header/common/content_type.rs
Normal file
27
src/header/common/content_type.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use header::Header;
|
||||
use std::fmt::{mod, Show};
|
||||
use super::from_one_raw_str;
|
||||
use mime::Mime;
|
||||
|
||||
/// The `Content-Type` header.
|
||||
///
|
||||
/// Used to describe the MIME type of message body. Can be used with both
|
||||
/// requests and responses.
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct ContentType(pub Mime);
|
||||
|
||||
impl Header for ContentType {
|
||||
fn header_name(_: Option<ContentType>) -> &'static str {
|
||||
"content-type"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<ContentType> {
|
||||
from_one_raw_str(raw).map(|mime| ContentType(mime))
|
||||
}
|
||||
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let ContentType(ref value) = *self;
|
||||
value.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
66
src/header/common/date.rs
Normal file
66
src/header/common/date.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use header::Header;
|
||||
use std::fmt::{mod, Show};
|
||||
use super::from_one_raw_str;
|
||||
use std::from_str::FromStr;
|
||||
use time::{Tm, strptime};
|
||||
|
||||
// Egh, replace as soon as something better than time::Tm exists.
|
||||
/// The `Date` header field.
|
||||
#[deriving(PartialEq, Clone)]
|
||||
pub struct Date(pub Tm);
|
||||
|
||||
impl Header for Date {
|
||||
fn header_name(_: Option<Date>) -> &'static str {
|
||||
"date"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<Date> {
|
||||
from_one_raw_str(raw)
|
||||
}
|
||||
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for Date {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Date(ref tm) = *self;
|
||||
// bummer that tm.strftime allocates a string. It would nice if it
|
||||
// returned a Show instead, since I don't need the String here
|
||||
write!(fmt, "{}", tm.to_utc().rfc822())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Date {
|
||||
// Prior to 1995, there were three different formats commonly used by
|
||||
// servers to communicate timestamps. For compatibility with old
|
||||
// implementations, all three are defined here. The preferred format is
|
||||
// a fixed-length and single-zone subset of the date and time
|
||||
// specification used by the Internet Message Format [RFC5322].
|
||||
//
|
||||
// HTTP-date = IMF-fixdate / obs-date
|
||||
//
|
||||
// An example of the preferred format is
|
||||
//
|
||||
// Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate
|
||||
//
|
||||
// Examples of the two obsolete formats are
|
||||
//
|
||||
// Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format
|
||||
// Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
|
||||
//
|
||||
// A recipient that parses a timestamp value in an HTTP header field
|
||||
// MUST accept all three HTTP-date formats. When a sender generates a
|
||||
// header field that contains one or more timestamps defined as
|
||||
// HTTP-date, the sender MUST generate those timestamps in the
|
||||
// IMF-fixdate format.
|
||||
fn from_str(s: &str) -> Option<Date> {
|
||||
strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| {
|
||||
strptime(s, "%A, %d-%b-%y %T %Z")
|
||||
}).or_else(|_| {
|
||||
strptime(s, "%c")
|
||||
}).ok().map(|tm| Date(tm))
|
||||
}
|
||||
}
|
||||
|
||||
29
src/header/common/host.rs
Normal file
29
src/header/common/host.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use header::Header;
|
||||
use std::fmt::{mod, Show};
|
||||
use super::from_one_raw_str;
|
||||
|
||||
/// The `Host` header.
|
||||
///
|
||||
/// HTTP/1.1 requires that all requests include a `Host` header, and so hyper
|
||||
/// client requests add one automatically.
|
||||
///
|
||||
/// Currently is just a String, but it should probably become a better type,
|
||||
/// like url::Host or something.
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct Host(pub String);
|
||||
|
||||
impl Header for Host {
|
||||
fn header_name(_: Option<Host>) -> &'static str {
|
||||
"host"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<Host> {
|
||||
from_one_raw_str(raw).map(|s| Host(s))
|
||||
}
|
||||
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Host(ref value) = *self;
|
||||
value.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
59
src/header/common/mod.rs
Normal file
59
src/header/common/mod.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
//! A Collection of Header implementations for common HTTP Headers.
|
||||
//!
|
||||
//! ## Mime
|
||||
//!
|
||||
//! Several header fields use MIME values for their contents. Keeping with the
|
||||
//! strongly-typed theme, the [mime](http://seanmonstar.github.io/mime.rs) crate
|
||||
//! is used, such as `ContentType(pub Mime)`.
|
||||
|
||||
pub use self::host::Host;
|
||||
pub use self::content_length::ContentLength;
|
||||
pub use self::content_type::ContentType;
|
||||
pub use self::accept::Accept;
|
||||
pub use self::connection::Connection;
|
||||
pub use self::transfer_encoding::TransferEncoding;
|
||||
pub use self::user_agent::UserAgent;
|
||||
pub use self::server::Server;
|
||||
pub use self::date::Date;
|
||||
|
||||
use std::from_str::FromStr;
|
||||
use std::str::from_utf8;
|
||||
|
||||
/// Exposes the Host header.
|
||||
pub mod host;
|
||||
|
||||
/// Exposes the ContentLength header.
|
||||
pub mod content_length;
|
||||
|
||||
/// Exposes the ContentType header.
|
||||
pub mod content_type;
|
||||
|
||||
/// Exposes the Accept header.
|
||||
pub mod accept;
|
||||
|
||||
/// Exposes the Connection header.
|
||||
pub mod connection;
|
||||
|
||||
/// Exposes the TransferEncoding header.
|
||||
pub mod transfer_encoding;
|
||||
|
||||
/// Exposes the UserAgent header.
|
||||
pub mod user_agent;
|
||||
|
||||
/// Exposes the Server header.
|
||||
pub mod server;
|
||||
|
||||
/// Exposes the Date header.
|
||||
pub mod date;
|
||||
|
||||
fn from_one_raw_str<T: FromStr>(raw: &[Vec<u8>]) -> Option<T> {
|
||||
if raw.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
// we JUST checked that raw.len() == 1, so raw[0] WILL exist.
|
||||
match from_utf8(unsafe { raw.as_slice().unsafe_get(0).as_slice() }) {
|
||||
Some(s) => FromStr::from_str(s),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
25
src/header/common/server.rs
Normal file
25
src/header/common/server.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use header::Header;
|
||||
use std::fmt::{mod, Show};
|
||||
use super::from_one_raw_str;
|
||||
|
||||
/// The `Server` header field.
|
||||
///
|
||||
/// They can contain any value, so it just wraps a `String`.
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct Server(pub String);
|
||||
|
||||
impl Header for Server {
|
||||
fn header_name(_: Option<Server>) -> &'static str {
|
||||
"server"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<Server> {
|
||||
from_one_raw_str(raw).map(|s| Server(s))
|
||||
}
|
||||
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Server(ref value) = *self;
|
||||
value.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
89
src/header/common/transfer_encoding.rs
Normal file
89
src/header/common/transfer_encoding.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use header::Header;
|
||||
use std::fmt::{mod, Show};
|
||||
use std::from_str::FromStr;
|
||||
use std::str::from_utf8;
|
||||
|
||||
/// The `Transfer-Encoding` header.
|
||||
///
|
||||
/// This header describes the encoding of the message body. It can be
|
||||
/// comma-separated, including multiple encodings.
|
||||
///
|
||||
/// ```notrust
|
||||
/// Transfer-Encoding: gzip, chunked
|
||||
/// ```
|
||||
///
|
||||
/// According to the spec, if a `Content-Length` header is not included,
|
||||
/// this header should include `chunked` as the last encoding.
|
||||
///
|
||||
/// The implementation uses a vector of `Encoding` values.
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct TransferEncoding(pub Vec<Encoding>);
|
||||
|
||||
/// A value to be used with the `Transfer-Encoding` header.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// # use hyper::header::common::transfer_encoding::{TransferEncoding, Gzip, Chunked};
|
||||
/// # use hyper::header::Headers;
|
||||
/// # let mut headers = Headers::new();
|
||||
/// headers.set(TransferEncoding(vec![Gzip, Chunked]));
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub enum Encoding {
|
||||
/// The `chunked` encoding.
|
||||
Chunked,
|
||||
|
||||
// TODO: #2 implement this in `HttpReader`.
|
||||
/// The `gzip` encoding.
|
||||
Gzip,
|
||||
/// The `deflate` encoding.
|
||||
Deflate,
|
||||
/// The `compress` encoding.
|
||||
Compress,
|
||||
/// Some other encoding that is less common, can be any String.
|
||||
EncodingExt(String)
|
||||
}
|
||||
|
||||
impl FromStr for Encoding {
|
||||
fn from_str(s: &str) -> Option<Encoding> {
|
||||
match s {
|
||||
"chunked" => Some(Chunked),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Header for TransferEncoding {
|
||||
fn header_name(_: Option<TransferEncoding>) -> &'static str {
|
||||
"transfer-encoding"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<TransferEncoding> {
|
||||
if raw.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
// we JUST checked that raw.len() == 1, so raw[0] WILL exist.
|
||||
match from_utf8(unsafe { raw.as_slice().unsafe_get(0).as_slice() }) {
|
||||
Some(s) => {
|
||||
Some(TransferEncoding(s.as_slice()
|
||||
.split([',', ' '].as_slice())
|
||||
.filter_map(from_str)
|
||||
.collect()))
|
||||
}
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let TransferEncoding(ref parts) = *self;
|
||||
let last = parts.len() - 1;
|
||||
for (i, part) in parts.iter().enumerate() {
|
||||
try!(part.fmt(fmt));
|
||||
if i < last {
|
||||
try!(", ".fmt(fmt));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
25
src/header/common/user_agent.rs
Normal file
25
src/header/common/user_agent.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use header::Header;
|
||||
use std::fmt::{mod, Show};
|
||||
use super::from_one_raw_str;
|
||||
|
||||
/// The `User-Agent` header field.
|
||||
///
|
||||
/// They can contain any value, so it just wraps a `String`.
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct UserAgent(pub String);
|
||||
|
||||
impl Header for UserAgent {
|
||||
fn header_name(_: Option<UserAgent>) -> &'static str {
|
||||
"user-agent"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<UserAgent> {
|
||||
from_one_raw_str(raw).map(|s| UserAgent(s))
|
||||
}
|
||||
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let UserAgent(ref value) = *self;
|
||||
value.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user