Returning cache headers for network content (#3123)
This commit is contained in:
parent
3a1f3c0a80
commit
e9cd2f4d56
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -352,6 +352,7 @@ dependencies = [
|
|||||||
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"zip 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zip 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -24,6 +24,7 @@ ethabi = "0.2.2"
|
|||||||
linked-hash-map = "0.3"
|
linked-hash-map = "0.3"
|
||||||
parity-dapps-glue = "1.4"
|
parity-dapps-glue = "1.4"
|
||||||
mime = "0.2"
|
mime = "0.2"
|
||||||
|
time = "0.1.35"
|
||||||
serde_macros = { version = "0.8", optional = true }
|
serde_macros = { version = "0.8", optional = true }
|
||||||
zip = { version = "0.1", default-features = false }
|
zip = { version = "0.1", default-features = false }
|
||||||
ethcore-devtools = { path = "../devtools" }
|
ethcore-devtools = { path = "../devtools" }
|
||||||
|
@ -32,14 +32,15 @@ use random_filename;
|
|||||||
use SyncStatus;
|
use SyncStatus;
|
||||||
use util::{Mutex, H256};
|
use util::{Mutex, H256};
|
||||||
use util::sha3::sha3;
|
use util::sha3::sha3;
|
||||||
use page::LocalPageEndpoint;
|
use page::{LocalPageEndpoint, PageCache};
|
||||||
use handlers::{ContentHandler, ContentFetcherHandler, ContentValidator};
|
use handlers::{ContentHandler, ContentFetcherHandler, ContentValidator};
|
||||||
use endpoint::{Endpoint, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointPath, Handler};
|
||||||
use apps::cache::{ContentCache, ContentStatus};
|
use apps::cache::{ContentCache, ContentStatus};
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||||
use apps::urlhint::{URLHintContract, URLHint, URLHintResult};
|
use apps::urlhint::{URLHintContract, URLHint, URLHintResult};
|
||||||
|
|
||||||
const MAX_CACHED_DAPPS: usize = 10;
|
/// Limit of cached dapps/content
|
||||||
|
const MAX_CACHED_DAPPS: usize = 20;
|
||||||
|
|
||||||
pub struct ContentFetcher<R: URLHint = URLHintContract> {
|
pub struct ContentFetcher<R: URLHint = URLHintContract> {
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
@ -259,6 +260,17 @@ impl ContentValidator for ContentInstaller {
|
|||||||
// Create dir
|
// Create dir
|
||||||
try!(fs::create_dir_all(&self.content_path));
|
try!(fs::create_dir_all(&self.content_path));
|
||||||
|
|
||||||
|
// Validate hash
|
||||||
|
let mut file_reader = io::BufReader::new(try!(fs::File::open(&path)));
|
||||||
|
let hash = try!(sha3(&mut file_reader));
|
||||||
|
let id = try!(self.id.as_str().parse().map_err(|_| ValidationError::InvalidContentId));
|
||||||
|
if id != hash {
|
||||||
|
return Err(ValidationError::HashMismatch {
|
||||||
|
expected: id,
|
||||||
|
got: hash,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// And prepare path for a file
|
// And prepare path for a file
|
||||||
let filename = path.file_name().expect("We always fetch a file.");
|
let filename = path.file_name().expect("We always fetch a file.");
|
||||||
let mut content_path = self.content_path.clone();
|
let mut content_path = self.content_path.clone();
|
||||||
@ -270,7 +282,7 @@ impl ContentValidator for ContentInstaller {
|
|||||||
|
|
||||||
try!(fs::copy(&path, &content_path));
|
try!(fs::copy(&path, &content_path));
|
||||||
|
|
||||||
Ok((self.id.clone(), LocalPageEndpoint::single_file(content_path, self.mime.clone())))
|
Ok((self.id.clone(), LocalPageEndpoint::single_file(content_path, self.mime.clone(), PageCache::Enabled)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn done(&self, endpoint: Option<LocalPageEndpoint>) {
|
fn done(&self, endpoint: Option<LocalPageEndpoint>) {
|
||||||
@ -376,7 +388,7 @@ impl ContentValidator for DappInstaller {
|
|||||||
try!(manifest_file.write_all(manifest_str.as_bytes()));
|
try!(manifest_file.write_all(manifest_str.as_bytes()));
|
||||||
|
|
||||||
// Create endpoint
|
// Create endpoint
|
||||||
let app = LocalPageEndpoint::new(target, manifest.clone().into(), self.embeddable_at);
|
let app = LocalPageEndpoint::new(target, manifest.clone().into(), PageCache::Enabled, self.embeddable_at);
|
||||||
|
|
||||||
// Return modified app manifest
|
// Return modified app manifest
|
||||||
Ok((manifest.id.clone(), app))
|
Ok((manifest.id.clone(), app))
|
||||||
@ -416,7 +428,7 @@ mod tests {
|
|||||||
version: "".into(),
|
version: "".into(),
|
||||||
author: "".into(),
|
author: "".into(),
|
||||||
icon_url: "".into(),
|
icon_url: "".into(),
|
||||||
}, None);
|
}, Default::default(), None);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
fetcher.set_status("test", ContentStatus::Ready(handler));
|
fetcher.set_status("test", ContentStatus::Ready(handler));
|
||||||
|
@ -18,7 +18,7 @@ use std::io;
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use page::LocalPageEndpoint;
|
use page::{LocalPageEndpoint, PageCache};
|
||||||
use endpoint::{Endpoints, EndpointInfo};
|
use endpoint::{Endpoints, EndpointInfo};
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ pub fn local_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoint
|
|||||||
for dapp in local_dapps(dapps_path) {
|
for dapp in local_dapps(dapps_path) {
|
||||||
pages.insert(
|
pages.insert(
|
||||||
dapp.id,
|
dapp.id,
|
||||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, signer_port))
|
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_port))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pages
|
pages
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||||
|
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
extern crate time;
|
||||||
extern crate url as url_lib;
|
extern crate url as url_lib;
|
||||||
extern crate unicase;
|
extern crate unicase;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use page::handler;
|
use page::{handler, PageCache};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||||
use parity_dapps::{WebApp, File, Info};
|
use parity_dapps::{WebApp, File, Info};
|
||||||
@ -80,6 +80,7 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
|
|||||||
prefix: self.prefix.clone(),
|
prefix: self.prefix.clone(),
|
||||||
path: path,
|
path: path,
|
||||||
file: handler::ServedFile::new(self.safe_to_embed_at_port.clone()),
|
file: handler::ServedFile::new(self.safe_to_embed_at_port.clone()),
|
||||||
|
cache: PageCache::Disabled,
|
||||||
safe_to_embed_at_port: self.safe_to_embed_at_port.clone(),
|
safe_to_embed_at_port: self.safe_to_embed_at_port.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use time::{self, Duration};
|
||||||
|
|
||||||
use hyper::header;
|
use hyper::header;
|
||||||
use hyper::server;
|
use hyper::server;
|
||||||
use hyper::uri::RequestUri;
|
use hyper::uri::RequestUri;
|
||||||
@ -69,6 +71,19 @@ impl<T: Dapp> ServedFile<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines what cache headers should be appended to returned resources.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum PageCache {
|
||||||
|
Enabled,
|
||||||
|
Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PageCache {
|
||||||
|
fn default() -> Self {
|
||||||
|
PageCache::Disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A handler for a single webapp.
|
/// A handler for a single webapp.
|
||||||
/// Resolves correct paths and serves as a plumbing code between
|
/// Resolves correct paths and serves as a plumbing code between
|
||||||
/// hyper server and dapp.
|
/// hyper server and dapp.
|
||||||
@ -83,6 +98,8 @@ pub struct PageHandler<T: Dapp> {
|
|||||||
pub path: EndpointPath,
|
pub path: EndpointPath,
|
||||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||||
pub safe_to_embed_at_port: Option<u16>,
|
pub safe_to_embed_at_port: Option<u16>,
|
||||||
|
/// Cache settings for this page.
|
||||||
|
pub cache: PageCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Dapp> PageHandler<T> {
|
impl<T: Dapp> PageHandler<T> {
|
||||||
@ -129,9 +146,19 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
|||||||
ServedFile::File(ref f) => {
|
ServedFile::File(ref f) => {
|
||||||
res.set_status(StatusCode::Ok);
|
res.set_status(StatusCode::Ok);
|
||||||
|
|
||||||
|
if let PageCache::Enabled = self.cache {
|
||||||
|
let mut headers = res.headers_mut();
|
||||||
|
let validity = Duration::days(365);
|
||||||
|
headers.set(header::CacheControl(vec![
|
||||||
|
header::CacheDirective::Public,
|
||||||
|
header::CacheDirective::MaxAge(validity.num_seconds() as u32),
|
||||||
|
]));
|
||||||
|
headers.set(header::Expires(header::HttpDate(time::now() + validity)));
|
||||||
|
}
|
||||||
|
|
||||||
match f.content_type().parse() {
|
match f.content_type().parse() {
|
||||||
Ok(mime) => res.headers_mut().set(header::ContentType(mime)),
|
Ok(mime) => res.headers_mut().set(header::ContentType(mime)),
|
||||||
Err(()) => debug!(target: "page_handler", "invalid MIME type: {}", f.content_type()),
|
Err(()) => debug!(target: "dapps", "invalid MIME type: {}", f.content_type()),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Security headers:
|
// Security headers:
|
||||||
@ -218,6 +245,7 @@ fn should_extract_path_with_appid() {
|
|||||||
using_dapps_domains: true,
|
using_dapps_domains: true,
|
||||||
},
|
},
|
||||||
file: ServedFile::new(None),
|
file: ServedFile::new(None),
|
||||||
|
cache: Default::default(),
|
||||||
safe_to_embed_at_port: None,
|
safe_to_embed_at_port: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ use mime_guess;
|
|||||||
use std::io::{Seek, Read, SeekFrom};
|
use std::io::{Seek, Read, SeekFrom};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use page::handler;
|
use page::handler::{self, PageCache};
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -26,24 +26,27 @@ pub struct LocalPageEndpoint {
|
|||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
mime: Option<String>,
|
mime: Option<String>,
|
||||||
info: Option<EndpointInfo>,
|
info: Option<EndpointInfo>,
|
||||||
|
cache: PageCache,
|
||||||
embeddable_at: Option<u16>,
|
embeddable_at: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalPageEndpoint {
|
impl LocalPageEndpoint {
|
||||||
pub fn new(path: PathBuf, info: EndpointInfo, embeddable_at: Option<u16>) -> Self {
|
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_at: Option<u16>) -> Self {
|
||||||
LocalPageEndpoint {
|
LocalPageEndpoint {
|
||||||
path: path,
|
path: path,
|
||||||
mime: None,
|
mime: None,
|
||||||
info: Some(info),
|
info: Some(info),
|
||||||
|
cache: cache,
|
||||||
embeddable_at: embeddable_at,
|
embeddable_at: embeddable_at,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn single_file(path: PathBuf, mime: String) -> Self {
|
pub fn single_file(path: PathBuf, mime: String, cache: PageCache) -> Self {
|
||||||
LocalPageEndpoint {
|
LocalPageEndpoint {
|
||||||
path: path,
|
path: path,
|
||||||
mime: Some(mime),
|
mime: Some(mime),
|
||||||
info: None,
|
info: None,
|
||||||
|
cache: cache,
|
||||||
embeddable_at: None,
|
embeddable_at: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,6 +69,7 @@ impl Endpoint for LocalPageEndpoint {
|
|||||||
path: path,
|
path: path,
|
||||||
file: handler::ServedFile::new(None),
|
file: handler::ServedFile::new(None),
|
||||||
safe_to_embed_at_port: self.embeddable_at,
|
safe_to_embed_at_port: self.embeddable_at,
|
||||||
|
cache: self.cache,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Box::new(handler::PageHandler {
|
Box::new(handler::PageHandler {
|
||||||
@ -74,6 +78,7 @@ impl Endpoint for LocalPageEndpoint {
|
|||||||
path: path,
|
path: path,
|
||||||
file: handler::ServedFile::new(None),
|
file: handler::ServedFile::new(None),
|
||||||
safe_to_embed_at_port: self.embeddable_at,
|
safe_to_embed_at_port: self.embeddable_at,
|
||||||
|
cache: self.cache,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,4 +21,5 @@ mod handler;
|
|||||||
|
|
||||||
pub use self::local::LocalPageEndpoint;
|
pub use self::local::LocalPageEndpoint;
|
||||||
pub use self::builtin::PageEndpoint;
|
pub use self::builtin::PageEndpoint;
|
||||||
|
pub use self::handler::PageCache;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user