mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-27 17:22:57 +01:00
split http request; add HttpRequest::range()
This commit is contained in:
parent
f30aef404d
commit
41f1e6cdc9
@ -30,10 +30,13 @@ nightly = []
|
||||
time = "0.1"
|
||||
http = "0.1"
|
||||
httparse = "0.1"
|
||||
http-range = "0.1"
|
||||
cookie = { version="0.10", features=["percent-encode"] }
|
||||
regex = "0.2"
|
||||
slab = "0.4"
|
||||
sha1 = "0.2"
|
||||
url = "1.5"
|
||||
lazy_static = "0.2"
|
||||
route-recognizer = "0.1"
|
||||
|
||||
# tokio
|
||||
|
@ -9,7 +9,7 @@ use route::RouteHandler;
|
||||
use router::Handler;
|
||||
use resource::Resource;
|
||||
use payload::Payload;
|
||||
use httpmessage::HttpRequest;
|
||||
use httprequest::HttpRequest;
|
||||
|
||||
|
||||
/// Application
|
||||
|
16
src/dev.rs
16
src/dev.rs
@ -9,13 +9,23 @@
|
||||
//! ```
|
||||
pub use ws;
|
||||
pub use httpcodes;
|
||||
pub use error::ParseError;
|
||||
pub use application::Application;
|
||||
pub use httpmessage::{Body, Builder, HttpRequest, HttpResponse};
|
||||
pub use payload::{Payload, PayloadItem};
|
||||
pub use httprequest::HttpRequest;
|
||||
pub use httpmessage::{Body, Builder, HttpResponse};
|
||||
pub use payload::{Payload, PayloadItem, PayloadError};
|
||||
pub use router::RoutingMap;
|
||||
pub use resource::{Reply, Resource};
|
||||
pub use route::{Route, RouteFactory, RouteHandler};
|
||||
pub use server::HttpServer;
|
||||
pub use context::HttpContext;
|
||||
pub use task::Task;
|
||||
pub use staticfiles::StaticFiles;
|
||||
|
||||
// re-exports
|
||||
pub use cookie::{Cookie, CookieBuilder};
|
||||
pub use cookie::{ParseError as CookieParseError};
|
||||
pub use route_recognizer::Params;
|
||||
pub use http_range::{HttpRange, HttpRangeParseError};
|
||||
|
||||
// dev specific
|
||||
pub use task::Task;
|
||||
|
10
src/error.rs
10
src/error.rs
@ -9,8 +9,10 @@ use cookie;
|
||||
use httparse;
|
||||
use http::{StatusCode, Error as HttpError};
|
||||
|
||||
use HttpRangeParseError;
|
||||
use httpmessage::{Body, HttpResponse};
|
||||
|
||||
|
||||
/// A set of errors that can occur during parsing HTTP streams.
|
||||
#[derive(Debug)]
|
||||
pub enum ParseError {
|
||||
@ -129,6 +131,14 @@ impl From<cookie::ParseError> for HttpResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `BadRequest` for `HttpRangeParseError`
|
||||
impl From<HttpRangeParseError> for HttpResponse {
|
||||
fn from(_: HttpRangeParseError) -> Self {
|
||||
HttpResponse::new(StatusCode::BAD_REQUEST,
|
||||
Body::Binary("Invalid Range header provided".into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::error::Error as StdError;
|
||||
|
@ -6,13 +6,15 @@ use http::StatusCode;
|
||||
use task::Task;
|
||||
use route::RouteHandler;
|
||||
use payload::Payload;
|
||||
use httpmessage::{Body, Builder, HttpRequest, HttpResponse};
|
||||
use httprequest::HttpRequest;
|
||||
use httpmessage::{Body, Builder, HttpResponse};
|
||||
|
||||
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
|
||||
pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED);
|
||||
pub const HTTPNoContent: StaticResponse = StaticResponse(StatusCode::NO_CONTENT);
|
||||
pub const HTTPBadRequest: StaticResponse = StaticResponse(StatusCode::BAD_REQUEST);
|
||||
pub const HTTPNotFound: StaticResponse = StaticResponse(StatusCode::NOT_FOUND);
|
||||
pub const HTTPForbidden: StaticResponse = StaticResponse(StatusCode::FORBIDDEN);
|
||||
pub const HTTPMethodNotAllowed: StaticResponse = StaticResponse(StatusCode::METHOD_NOT_ALLOWED);
|
||||
pub const HTTPInternalServerError: StaticResponse =
|
||||
StaticResponse(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
@ -4,177 +4,23 @@ use std::convert::Into;
|
||||
|
||||
use cookie::CookieJar;
|
||||
use bytes::Bytes;
|
||||
use http::{Method, StatusCode, Version, Uri, HeaderMap, HttpTryFrom, Error};
|
||||
use http::{StatusCode, Version, HeaderMap, HttpTryFrom, Error};
|
||||
use http::header::{self, HeaderName, HeaderValue};
|
||||
|
||||
use Params;
|
||||
use {Cookie, CookieParseError};
|
||||
use Cookie;
|
||||
|
||||
|
||||
/// Represents various types of connection
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum ConnectionType {
|
||||
/// Close connection after response
|
||||
Close,
|
||||
/// Keep connection alive after response
|
||||
KeepAlive,
|
||||
/// Connection is upgraded to different type
|
||||
Upgrade,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// An HTTP Request
|
||||
pub struct HttpRequest {
|
||||
version: Version,
|
||||
method: Method,
|
||||
uri: Uri,
|
||||
headers: HeaderMap,
|
||||
params: Params,
|
||||
cookies: Vec<Cookie<'static>>,
|
||||
}
|
||||
|
||||
impl HttpRequest {
|
||||
/// Construct a new Request.
|
||||
#[inline]
|
||||
pub fn new(method: Method, uri: Uri, version: Version, headers: HeaderMap) -> Self {
|
||||
HttpRequest {
|
||||
method: method,
|
||||
uri: uri,
|
||||
version: version,
|
||||
headers: headers,
|
||||
params: Params::new(),
|
||||
cookies: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Request Uri.
|
||||
#[inline]
|
||||
pub fn uri(&self) -> &Uri { &self.uri }
|
||||
|
||||
/// Read the Request method.
|
||||
#[inline]
|
||||
pub fn method(&self) -> &Method { &self.method }
|
||||
|
||||
/// Read the Request Version.
|
||||
pub fn version(&self) -> Version {
|
||||
self.version
|
||||
}
|
||||
|
||||
/// Read the Request Headers.
|
||||
pub fn headers(&self) -> &HeaderMap {
|
||||
&self.headers
|
||||
}
|
||||
|
||||
// /// The remote socket address of this request
|
||||
// ///
|
||||
// /// This is an `Option`, because some underlying transports may not have
|
||||
// /// a socket address, such as Unix Sockets.
|
||||
// ///
|
||||
// /// This field is not used for outgoing requests.
|
||||
// #[inline]
|
||||
// pub fn remote_addr(&self) -> Option<SocketAddr> { self.remote_addr }
|
||||
|
||||
/// The target path of this Request.
|
||||
#[inline]
|
||||
pub fn path(&self) -> &str {
|
||||
self.uri.path()
|
||||
}
|
||||
|
||||
/// The query string of this Request.
|
||||
#[inline]
|
||||
pub fn query(&self) -> Option<&str> {
|
||||
self.uri.query()
|
||||
}
|
||||
|
||||
/// Return request cookies.
|
||||
pub fn cookies(&self) -> &Vec<Cookie<'static>> {
|
||||
&self.cookies
|
||||
}
|
||||
|
||||
/// Return request cookie.
|
||||
pub fn cookie(&self, name: &str) -> Option<&Cookie> {
|
||||
for cookie in &self.cookies {
|
||||
if cookie.name() == name {
|
||||
return Some(&cookie)
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Load cookies
|
||||
pub fn load_cookies(&mut self) -> Result<&Vec<Cookie>, CookieParseError>
|
||||
{
|
||||
if let Some(val) = self.headers.get(header::COOKIE) {
|
||||
let s = str::from_utf8(val.as_bytes())
|
||||
.map_err(CookieParseError::from)?;
|
||||
for cookie in s.split("; ") {
|
||||
self.cookies.push(Cookie::parse_encoded(cookie)?.into_owned());
|
||||
}
|
||||
}
|
||||
Ok(&self.cookies)
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the Request headers.
|
||||
#[inline]
|
||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||
&mut self.headers
|
||||
}
|
||||
|
||||
/// Get a reference to the Params object.
|
||||
/// Params is a container for url parameters.
|
||||
/// Route supports glob patterns: * for a single wildcard segment and :param
|
||||
/// for matching storing that segment of the request url in the Params object.
|
||||
#[inline]
|
||||
pub fn params(&self) -> &Params { &self.params }
|
||||
|
||||
/// Create new request with Params object.
|
||||
pub fn with_params(self, params: Params) -> Self {
|
||||
HttpRequest {
|
||||
method: self.method,
|
||||
uri: self.uri,
|
||||
version: self.version,
|
||||
headers: self.headers,
|
||||
params: params,
|
||||
cookies: self.cookies,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a connection should be kept alive.
|
||||
pub fn keep_alive(&self) -> bool {
|
||||
if let Some(conn) = self.headers.get(header::CONNECTION) {
|
||||
if let Ok(conn) = conn.to_str() {
|
||||
if self.version == Version::HTTP_10 && conn.contains("keep-alive") {
|
||||
true
|
||||
} else {
|
||||
self.version == Version::HTTP_11 &&
|
||||
!(conn.contains("close") || conn.contains("upgrade"))
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
self.version != Version::HTTP_10
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn upgrade(&self) -> bool {
|
||||
if let Some(conn) = self.headers().get(header::CONNECTION) {
|
||||
if let Ok(s) = conn.to_str() {
|
||||
return s.to_lowercase().contains("upgrade")
|
||||
}
|
||||
}
|
||||
self.method == Method::CONNECT
|
||||
}
|
||||
|
||||
pub fn chunked(&self) -> Result<bool, io::Error> {
|
||||
if let Some(encodings) = self.headers().get(header::TRANSFER_ENCODING) {
|
||||
if let Ok(s) = encodings.to_str() {
|
||||
Ok(s.to_lowercase().contains("chunked"))
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other, "Can not read transfer-encoding header"))
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents various types of http message body.
|
||||
#[derive(Debug)]
|
||||
pub enum Body {
|
||||
|
178
src/httprequest.rs
Normal file
178
src/httprequest.rs
Normal file
@ -0,0 +1,178 @@
|
||||
//! Pieces pertaining to the HTTP message protocol.
|
||||
use std::{io, str};
|
||||
use http::{header, Method, Version, Uri, HeaderMap};
|
||||
|
||||
use Params;
|
||||
use {Cookie, CookieParseError};
|
||||
use {HttpRange, HttpRangeParseError};
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
/// An HTTP Request
|
||||
pub struct HttpRequest {
|
||||
version: Version,
|
||||
method: Method,
|
||||
uri: Uri,
|
||||
headers: HeaderMap,
|
||||
params: Params,
|
||||
cookies: Vec<Cookie<'static>>,
|
||||
}
|
||||
|
||||
impl HttpRequest {
|
||||
/// Construct a new Request.
|
||||
#[inline]
|
||||
pub fn new(method: Method, uri: Uri, version: Version, headers: HeaderMap) -> Self {
|
||||
HttpRequest {
|
||||
method: method,
|
||||
uri: uri,
|
||||
version: version,
|
||||
headers: headers,
|
||||
params: Params::new(),
|
||||
cookies: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Request Uri.
|
||||
#[inline]
|
||||
pub fn uri(&self) -> &Uri { &self.uri }
|
||||
|
||||
/// Read the Request method.
|
||||
#[inline]
|
||||
pub fn method(&self) -> &Method { &self.method }
|
||||
|
||||
/// Read the Request Version.
|
||||
pub fn version(&self) -> Version {
|
||||
self.version
|
||||
}
|
||||
|
||||
/// Read the Request Headers.
|
||||
pub fn headers(&self) -> &HeaderMap {
|
||||
&self.headers
|
||||
}
|
||||
|
||||
// /// The remote socket address of this request
|
||||
// ///
|
||||
// /// This is an `Option`, because some underlying transports may not have
|
||||
// /// a socket address, such as Unix Sockets.
|
||||
// ///
|
||||
// /// This field is not used for outgoing requests.
|
||||
// #[inline]
|
||||
// pub fn remote_addr(&self) -> Option<SocketAddr> { self.remote_addr }
|
||||
|
||||
/// The target path of this Request.
|
||||
#[inline]
|
||||
pub fn path(&self) -> &str {
|
||||
self.uri.path()
|
||||
}
|
||||
|
||||
/// The query string of this Request.
|
||||
#[inline]
|
||||
pub fn query(&self) -> Option<&str> {
|
||||
self.uri.query()
|
||||
}
|
||||
|
||||
/// Return request cookies.
|
||||
pub fn cookies(&self) -> &Vec<Cookie<'static>> {
|
||||
&self.cookies
|
||||
}
|
||||
|
||||
/// Return request cookie.
|
||||
pub fn cookie(&self, name: &str) -> Option<&Cookie> {
|
||||
for cookie in &self.cookies {
|
||||
if cookie.name() == name {
|
||||
return Some(&cookie)
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Load cookies
|
||||
pub fn load_cookies(&mut self) -> Result<&Vec<Cookie>, CookieParseError>
|
||||
{
|
||||
if let Some(val) = self.headers.get(header::COOKIE) {
|
||||
let s = str::from_utf8(val.as_bytes())
|
||||
.map_err(CookieParseError::from)?;
|
||||
for cookie in s.split("; ") {
|
||||
self.cookies.push(Cookie::parse_encoded(cookie)?.into_owned());
|
||||
}
|
||||
}
|
||||
Ok(&self.cookies)
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the Request headers.
|
||||
#[inline]
|
||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||
&mut self.headers
|
||||
}
|
||||
|
||||
/// Get a reference to the Params object.
|
||||
/// Params is a container for url parameters.
|
||||
/// Route supports glob patterns: * for a single wildcard segment and :param
|
||||
/// for matching storing that segment of the request url in the Params object.
|
||||
#[inline]
|
||||
pub fn params(&self) -> &Params { &self.params }
|
||||
|
||||
/// Create new request with Params object.
|
||||
pub fn with_params(self, params: Params) -> Self {
|
||||
HttpRequest {
|
||||
method: self.method,
|
||||
uri: self.uri,
|
||||
version: self.version,
|
||||
headers: self.headers,
|
||||
params: params,
|
||||
cookies: self.cookies,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a connection should be kept alive.
|
||||
pub fn keep_alive(&self) -> bool {
|
||||
if let Some(conn) = self.headers.get(header::CONNECTION) {
|
||||
if let Ok(conn) = conn.to_str() {
|
||||
if self.version == Version::HTTP_10 && conn.contains("keep-alive") {
|
||||
true
|
||||
} else {
|
||||
self.version == Version::HTTP_11 &&
|
||||
!(conn.contains("close") || conn.contains("upgrade"))
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
self.version != Version::HTTP_10
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if request requires connection upgrade
|
||||
pub(crate) fn upgrade(&self) -> bool {
|
||||
if let Some(conn) = self.headers().get(header::CONNECTION) {
|
||||
if let Ok(s) = conn.to_str() {
|
||||
return s.to_lowercase().contains("upgrade")
|
||||
}
|
||||
}
|
||||
self.method == Method::CONNECT
|
||||
}
|
||||
|
||||
/// Check if request has chunked transfer encoding
|
||||
pub fn chunked(&self) -> Result<bool, io::Error> {
|
||||
if let Some(encodings) = self.headers().get(header::TRANSFER_ENCODING) {
|
||||
if let Ok(s) = encodings.to_str() {
|
||||
Ok(s.to_lowercase().contains("chunked"))
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other, "Can not read transfer-encoding header"))
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses Range HTTP header string as per RFC 2616.
|
||||
/// `size` is full size of response (file).
|
||||
pub fn range(&self, size: u64) -> Result<Vec<HttpRange>, HttpRangeParseError> {
|
||||
if let Some(range) = self.headers().get(header::RANGE) {
|
||||
HttpRange::parse(unsafe{str::from_utf8_unchecked(range.as_bytes())}, size)
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
16
src/lib.rs
16
src/lib.rs
@ -9,17 +9,21 @@ extern crate log;
|
||||
extern crate time;
|
||||
extern crate bytes;
|
||||
extern crate sha1;
|
||||
extern crate url;
|
||||
extern crate cookie;
|
||||
extern crate regex;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate tokio_core;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_proto;
|
||||
|
||||
extern crate cookie;
|
||||
extern crate http;
|
||||
extern crate httparse;
|
||||
extern crate http_range;
|
||||
extern crate route_recognizer;
|
||||
extern crate url;
|
||||
extern crate actix;
|
||||
|
||||
mod application;
|
||||
@ -27,6 +31,7 @@ mod context;
|
||||
mod error;
|
||||
mod date;
|
||||
mod decode;
|
||||
mod httprequest;
|
||||
mod httpmessage;
|
||||
mod payload;
|
||||
mod resource;
|
||||
@ -34,6 +39,7 @@ mod route;
|
||||
mod router;
|
||||
mod task;
|
||||
mod reader;
|
||||
mod staticfiles;
|
||||
mod server;
|
||||
mod wsframe;
|
||||
mod wsproto;
|
||||
@ -41,16 +47,20 @@ mod wsproto;
|
||||
pub mod ws;
|
||||
pub mod dev;
|
||||
pub mod httpcodes;
|
||||
pub use error::ParseError;
|
||||
pub use application::Application;
|
||||
pub use httpmessage::{Body, Builder, HttpRequest, HttpResponse};
|
||||
pub use httprequest::HttpRequest;
|
||||
pub use httpmessage::{Body, Builder, HttpResponse};
|
||||
pub use payload::{Payload, PayloadItem, PayloadError};
|
||||
pub use router::RoutingMap;
|
||||
pub use resource::{Reply, Resource};
|
||||
pub use route::{Route, RouteFactory, RouteHandler};
|
||||
pub use server::HttpServer;
|
||||
pub use context::HttpContext;
|
||||
pub use staticfiles::StaticFiles;
|
||||
|
||||
// re-exports
|
||||
pub use cookie::{Cookie, CookieBuilder};
|
||||
pub use cookie::{ParseError as CookieParseError};
|
||||
pub use route_recognizer::Params;
|
||||
pub use http_range::{HttpRange, HttpRangeParseError};
|
||||
|
@ -9,7 +9,7 @@ use tokio_io::AsyncRead;
|
||||
|
||||
use error::ParseError;
|
||||
use decode::Decoder;
|
||||
use httpmessage::HttpRequest;
|
||||
use httprequest::HttpRequest;
|
||||
use payload::{Payload, PayloadError, PayloadSender};
|
||||
|
||||
const MAX_HEADERS: usize = 100;
|
||||
|
@ -10,8 +10,9 @@ use task::Task;
|
||||
use route::{Route, RouteHandler};
|
||||
use payload::Payload;
|
||||
use context::HttpContext;
|
||||
use httprequest::HttpRequest;
|
||||
use httpmessage::HttpResponse;
|
||||
use httpcodes::HTTPMethodNotAllowed;
|
||||
use httpmessage::{HttpRequest, HttpResponse};
|
||||
|
||||
/// Http resource
|
||||
///
|
||||
@ -27,8 +28,7 @@ use httpmessage::{HttpRequest, HttpResponse};
|
||||
/// fn main() {
|
||||
/// let mut routes = RoutingMap::default();
|
||||
///
|
||||
/// routes
|
||||
/// .add_resource("/")
|
||||
/// routes.add_resource("/")
|
||||
/// .post::<MyRoute>();
|
||||
/// }
|
||||
pub struct Resource<S=()> {
|
||||
|
@ -8,7 +8,8 @@ use task::Task;
|
||||
use context::HttpContext;
|
||||
use resource::Reply;
|
||||
use payload::Payload;
|
||||
use httpmessage::{HttpRequest, HttpResponse};
|
||||
use httprequest::HttpRequest;
|
||||
use httpmessage::HttpResponse;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
|
@ -9,7 +9,7 @@ use route::RouteHandler;
|
||||
use resource::Resource;
|
||||
use application::Application;
|
||||
use httpcodes::HTTPNotFound;
|
||||
use httpmessage::HttpRequest;
|
||||
use httprequest::HttpRequest;
|
||||
|
||||
pub(crate) trait Handler: 'static {
|
||||
fn handle(&self, req: HttpRequest, payload: Payload) -> Task;
|
||||
|
23
src/staticfiles.rs
Normal file
23
src/staticfiles.rs
Normal file
@ -0,0 +1,23 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
use std::rc::Rc;
|
||||
|
||||
use task::Task;
|
||||
use route::RouteHandler;
|
||||
use payload::Payload;
|
||||
use httpcodes::HTTPOk;
|
||||
use httprequest::HttpRequest;
|
||||
|
||||
|
||||
pub struct StaticFiles {
|
||||
directory: String,
|
||||
show_index: bool,
|
||||
chunk_size: usize,
|
||||
follow_synlinks: bool,
|
||||
}
|
||||
|
||||
impl<S: 'static> RouteHandler<S> for StaticFiles {
|
||||
|
||||
fn handle(&self, req: HttpRequest, payload: Payload, state: Rc<S>) -> Task {
|
||||
Task::reply(HTTPOk)
|
||||
}
|
||||
}
|
@ -12,7 +12,8 @@ use tokio_core::net::TcpStream;
|
||||
|
||||
use date;
|
||||
use route::Frame;
|
||||
use httpmessage::{Body, HttpRequest, HttpResponse};
|
||||
use httprequest::HttpRequest;
|
||||
use httpmessage::{Body, HttpResponse};
|
||||
|
||||
type FrameStream = Stream<Item=Frame, Error=io::Error>;
|
||||
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
|
||||
|
@ -74,7 +74,8 @@ use context::HttpContext;
|
||||
use route::Route;
|
||||
use payload::Payload;
|
||||
use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed};
|
||||
use httpmessage::{Body, ConnectionType, HttpRequest, HttpResponse};
|
||||
use httprequest::HttpRequest;
|
||||
use httpmessage::{Body, ConnectionType, HttpResponse};
|
||||
|
||||
use wsframe;
|
||||
use wsproto::*;
|
||||
|
@ -72,3 +72,25 @@ fn test_response_cookies() {
|
||||
assert_eq!(
|
||||
val[1],"name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_request_range_header() {
|
||||
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
|
||||
Version::HTTP_11, HeaderMap::new());
|
||||
let ranges = req.range(100).unwrap();
|
||||
assert!(ranges.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_request_range_header() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(header::RANGE,
|
||||
header::HeaderValue::from_static("bytes=0-4"));
|
||||
|
||||
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
|
||||
Version::HTTP_11, headers);
|
||||
let ranges = req.range(100).unwrap();
|
||||
assert_eq!(ranges.len(), 1);
|
||||
assert_eq!(ranges[0].start, 0);
|
||||
assert_eq!(ranges[0].length, 5);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user