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_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)",
|
||||
"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)",
|
||||
"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)",
|
||||
|
@ -24,6 +24,7 @@ ethabi = "0.2.2"
|
||||
linked-hash-map = "0.3"
|
||||
parity-dapps-glue = "1.4"
|
||||
mime = "0.2"
|
||||
time = "0.1.35"
|
||||
serde_macros = { version = "0.8", optional = true }
|
||||
zip = { version = "0.1", default-features = false }
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
|
@ -32,14 +32,15 @@ use random_filename;
|
||||
use SyncStatus;
|
||||
use util::{Mutex, H256};
|
||||
use util::sha3::sha3;
|
||||
use page::LocalPageEndpoint;
|
||||
use page::{LocalPageEndpoint, PageCache};
|
||||
use handlers::{ContentHandler, ContentFetcherHandler, ContentValidator};
|
||||
use endpoint::{Endpoint, EndpointPath, Handler};
|
||||
use apps::cache::{ContentCache, ContentStatus};
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||
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> {
|
||||
dapps_path: PathBuf,
|
||||
@ -259,6 +260,17 @@ impl ContentValidator for ContentInstaller {
|
||||
// Create dir
|
||||
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
|
||||
let filename = path.file_name().expect("We always fetch a file.");
|
||||
let mut content_path = self.content_path.clone();
|
||||
@ -270,7 +282,7 @@ impl ContentValidator for ContentInstaller {
|
||||
|
||||
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>) {
|
||||
@ -376,7 +388,7 @@ impl ContentValidator for DappInstaller {
|
||||
try!(manifest_file.write_all(manifest_str.as_bytes()));
|
||||
|
||||
// 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
|
||||
Ok((manifest.id.clone(), app))
|
||||
@ -416,7 +428,7 @@ mod tests {
|
||||
version: "".into(),
|
||||
author: "".into(),
|
||||
icon_url: "".into(),
|
||||
}, None);
|
||||
}, Default::default(), None);
|
||||
|
||||
// when
|
||||
fetcher.set_status("test", ContentStatus::Ready(handler));
|
||||
|
@ -18,7 +18,7 @@ use std::io;
|
||||
use std::io::Read;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use page::LocalPageEndpoint;
|
||||
use page::{LocalPageEndpoint, PageCache};
|
||||
use endpoint::{Endpoints, EndpointInfo};
|
||||
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) {
|
||||
pages.insert(
|
||||
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
|
||||
|
@ -44,6 +44,7 @@
|
||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||
|
||||
extern crate hyper;
|
||||
extern crate time;
|
||||
extern crate url as url_lib;
|
||||
extern crate unicase;
|
||||
extern crate serde;
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use page::handler;
|
||||
use page::{handler, PageCache};
|
||||
use std::sync::Arc;
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||
use parity_dapps::{WebApp, File, Info};
|
||||
@ -80,6 +80,7 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
|
||||
prefix: self.prefix.clone(),
|
||||
path: path,
|
||||
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(),
|
||||
})
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io::Write;
|
||||
use time::{self, Duration};
|
||||
|
||||
use hyper::header;
|
||||
use hyper::server;
|
||||
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.
|
||||
/// Resolves correct paths and serves as a plumbing code between
|
||||
/// hyper server and dapp.
|
||||
@ -83,6 +98,8 @@ pub struct PageHandler<T: Dapp> {
|
||||
pub path: EndpointPath,
|
||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||
pub safe_to_embed_at_port: Option<u16>,
|
||||
/// Cache settings for this page.
|
||||
pub cache: PageCache,
|
||||
}
|
||||
|
||||
impl<T: Dapp> PageHandler<T> {
|
||||
@ -129,9 +146,19 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
||||
ServedFile::File(ref f) => {
|
||||
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() {
|
||||
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:
|
||||
@ -218,6 +245,7 @@ fn should_extract_path_with_appid() {
|
||||
using_dapps_domains: true,
|
||||
},
|
||||
file: ServedFile::new(None),
|
||||
cache: Default::default(),
|
||||
safe_to_embed_at_port: None,
|
||||
};
|
||||
|
||||
|
@ -18,7 +18,7 @@ use mime_guess;
|
||||
use std::io::{Seek, Read, SeekFrom};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use page::handler;
|
||||
use page::handler::{self, PageCache};
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -26,24 +26,27 @@ pub struct LocalPageEndpoint {
|
||||
path: PathBuf,
|
||||
mime: Option<String>,
|
||||
info: Option<EndpointInfo>,
|
||||
cache: PageCache,
|
||||
embeddable_at: Option<u16>,
|
||||
}
|
||||
|
||||
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 {
|
||||
path: path,
|
||||
mime: None,
|
||||
info: Some(info),
|
||||
cache: cache,
|
||||
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 {
|
||||
path: path,
|
||||
mime: Some(mime),
|
||||
info: None,
|
||||
cache: cache,
|
||||
embeddable_at: None,
|
||||
}
|
||||
}
|
||||
@ -66,6 +69,7 @@ impl Endpoint for LocalPageEndpoint {
|
||||
path: path,
|
||||
file: handler::ServedFile::new(None),
|
||||
safe_to_embed_at_port: self.embeddable_at,
|
||||
cache: self.cache,
|
||||
})
|
||||
} else {
|
||||
Box::new(handler::PageHandler {
|
||||
@ -74,6 +78,7 @@ impl Endpoint for LocalPageEndpoint {
|
||||
path: path,
|
||||
file: handler::ServedFile::new(None),
|
||||
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::builtin::PageEndpoint;
|
||||
pub use self::handler::PageCache;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user