diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/deno_dir.rs | 178 | ||||
-rw-r--r-- | src/http_util.rs | 24 | ||||
-rw-r--r-- | src/msg.fbs | 8 | ||||
-rw-r--r-- | src/ops.rs | 1 |
4 files changed, 199 insertions, 12 deletions
diff --git a/src/deno_dir.rs b/src/deno_dir.rs index e8cc86b18..186dce1b8 100644 --- a/src/deno_dir.rs +++ b/src/deno_dir.rs @@ -5,6 +5,7 @@ use errors::DenoResult; use errors::ErrorKind; use fs as deno_fs; use http_util; +use msg; use ring; use std; use std::fmt::Write; @@ -109,21 +110,28 @@ impl DenoDir { self: &DenoDir, module_name: &str, filename: &str, - ) -> DenoResult<String> { + ) -> DenoResult<(String, msg::MediaType)> { let p = Path::new(filename); + // We write a special ".mime" file into the `.deno/deps` directory along side the + // cached file, containing just the media type. + let mut media_type_filename = filename.to_string(); + media_type_filename.push_str(".mime"); + let mt = Path::new(&media_type_filename); let src = if self.reload || !p.exists() { println!("Downloading {}", module_name); - let source = http_util::fetch_sync_string(module_name)?; + let (source, content_type) = http_util::fetch_sync_string(module_name)?; match p.parent() { Some(ref parent) => fs::create_dir_all(parent), None => Ok(()), }?; deno_fs::write_file(&p, source.as_bytes(), 0o666)?; - source + deno_fs::write_file(&mt, content_type.as_bytes(), 0o666)?; + (source, map_content_type(&p, Some(&content_type))) } else { let source = fs::read_to_string(&p)?; - source + let content_type = fs::read_to_string(&mt)?; + (source, map_content_type(&p, Some(&content_type))) }; Ok(src) } @@ -141,18 +149,20 @@ impl DenoDir { let use_extension = |ext| { let module_name = format!("{}{}", module_name, ext); let filename = format!("{}{}", filename, ext); - let source_code = if is_module_remote { + let (source_code, media_type) = if is_module_remote { self.fetch_remote_source(&module_name, &filename)? } else { assert_eq!( module_name, filename, "if a module isn't remote, it should have the same filename" ); - fs::read_to_string(Path::new(&filename))? + let path = Path::new(&filename); + (fs::read_to_string(path)?, map_content_type(path, None)) }; return Ok(CodeFetchOutput { module_name: module_name.to_string(), filename: filename.to_string(), + media_type, source_code, maybe_output_code: None, }); @@ -215,6 +225,7 @@ impl DenoDir { Ok(output_code) => Ok(CodeFetchOutput { module_name: out.module_name, filename: out.filename, + media_type: out.media_type, source_code: out.source_code, maybe_output_code: Some(output_code), }), @@ -324,6 +335,7 @@ fn test_get_cache_filename() { pub struct CodeFetchOutput { pub module_name: String, pub filename: String, + pub media_type: msg::MediaType, pub source_code: String, pub maybe_output_code: Option<String>, } @@ -646,3 +658,157 @@ fn parse_local_or_remote(p: &str) -> Result<url::Url, url::ParseError> { Url::from_file_path(p).map_err(|_err| url::ParseError::IdnaError) } } + +fn map_file_extension(path: &Path) -> msg::MediaType { + match path.extension() { + None => msg::MediaType::Unknown, + Some(os_str) => match os_str.to_str() { + Some("ts") => msg::MediaType::TypeScript, + Some("js") => msg::MediaType::JavaScript, + Some("json") => msg::MediaType::Json, + _ => msg::MediaType::Unknown, + }, + } +} + +#[test] +fn test_map_file_extension() { + assert_eq!( + map_file_extension(Path::new("foo/bar.ts")), + msg::MediaType::TypeScript + ); + assert_eq!( + map_file_extension(Path::new("foo/bar.d.ts")), + msg::MediaType::TypeScript + ); + assert_eq!( + map_file_extension(Path::new("foo/bar.js")), + msg::MediaType::JavaScript + ); + assert_eq!( + map_file_extension(Path::new("foo/bar.json")), + msg::MediaType::Json + ); + assert_eq!( + map_file_extension(Path::new("foo/bar.txt")), + msg::MediaType::Unknown + ); + assert_eq!( + map_file_extension(Path::new("foo/bar")), + msg::MediaType::Unknown + ); +} + +// convert a ContentType string into a enumerated MediaType +fn map_content_type(path: &Path, content_type: Option<&str>) -> msg::MediaType { + match content_type { + Some(content_type) => { + // sometimes there is additional data after the media type in + // Content-Type so we have to do a bit of manipulation so we are only + // dealing with the actual media type + let ct_vector: Vec<&str> = content_type.split(";").collect(); + let ct: &str = ct_vector.first().unwrap(); + match ct.to_lowercase().as_ref() { + "application/typescript" + | "text/typescript" + | "video/vnd.dlna.mpeg-tts" + | "video/mp2t" => msg::MediaType::TypeScript, + "application/javascript" + | "text/javascript" + | "application/ecmascript" + | "text/ecmascript" + | "application/x-javascript" => msg::MediaType::JavaScript, + "application/json" | "text/json" => msg::MediaType::Json, + "text/plain" => map_file_extension(path), + _ => { + debug!("unknown content type: {}", content_type); + msg::MediaType::Unknown + } + } + } + None => map_file_extension(path), + } +} + +#[test] +fn test_map_content_type() { + // Extension only + assert_eq!( + map_content_type(Path::new("foo/bar.ts"), None), + msg::MediaType::TypeScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar.d.ts"), None), + msg::MediaType::TypeScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar.js"), None), + msg::MediaType::JavaScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar.json"), None), + msg::MediaType::Json + ); + assert_eq!( + map_content_type(Path::new("foo/bar.txt"), None), + msg::MediaType::Unknown + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), None), + msg::MediaType::Unknown + ); + + // Media Type + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("application/typescript")), + msg::MediaType::TypeScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("text/typescript")), + msg::MediaType::TypeScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("video/vnd.dlna.mpeg-tts")), + msg::MediaType::TypeScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("video/mp2t")), + msg::MediaType::TypeScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("application/javascript")), + msg::MediaType::JavaScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("text/javascript")), + msg::MediaType::JavaScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("application/ecmascript")), + msg::MediaType::JavaScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("text/ecmascript")), + msg::MediaType::JavaScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("application/x-javascript")), + msg::MediaType::JavaScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("application/json")), + msg::MediaType::Json + ); + assert_eq!( + map_content_type(Path::new("foo/bar"), Some("text/json")), + msg::MediaType::Json + ); + assert_eq!( + map_content_type(Path::new("foo/bar.ts"), Some("text/plain")), + msg::MediaType::TypeScript + ); + assert_eq!( + map_content_type(Path::new("foo/bar.ts"), Some("foo/bar")), + msg::MediaType::Unknown + ); +} diff --git a/src/http_util.rs b/src/http_util.rs index 1c6079c08..9c5ff9529 100644 --- a/src/http_util.rs +++ b/src/http_util.rs @@ -4,9 +4,10 @@ use errors::{DenoError, DenoResult}; use tokio_util; use futures::future::{loop_fn, Loop}; -use futures::{Future, Stream}; +use futures::{future, Future, Stream}; use hyper; use hyper::client::{Client, HttpConnector}; +use hyper::header::CONTENT_TYPE; use hyper::Uri; use hyper_rustls; @@ -27,7 +28,7 @@ pub fn get_client() -> Client<Connector, hyper::Body> { // The CodeFetch message is used to load HTTP javascript resources and expects a // synchronous response, this utility method supports that. -pub fn fetch_sync_string(module_name: &str) -> DenoResult<String> { +pub fn fetch_sync_string(module_name: &str) -> DenoResult<(String, String)> { let url = module_name.parse::<Uri>().unwrap(); let client = get_client(); // TODO(kevinkassimo): consider set a max redirection counter @@ -63,11 +64,18 @@ pub fn fetch_sync_string(module_name: &str) -> DenoResult<String> { Ok(Loop::Break(response)) }) }).and_then(|response| { - response + let content_type = response + .headers() + .get(CONTENT_TYPE) + .map(|content_type| content_type.to_str().unwrap().to_string()); + let body = response .into_body() .concat2() .map(|body| String::from_utf8(body.to_vec()).unwrap()) - .map_err(|err| DenoError::from(err)) + .map_err(|err| DenoError::from(err)); + body.join(future::ok(content_type)) + }).and_then(|(body_string, maybe_content_type)| { + future::ok((body_string, maybe_content_type.unwrap())) }); tokio_util::block_on(fetch_future) @@ -77,9 +85,11 @@ pub fn fetch_sync_string(module_name: &str) -> DenoResult<String> { fn test_fetch_sync_string() { // Relies on external http server. See tools/http_server.py tokio_util::init(|| { - let p = fetch_sync_string("http://127.0.0.1:4545/package.json").unwrap(); + let (p, m) = + fetch_sync_string("http://127.0.0.1:4545/package.json").unwrap(); println!("package.json len {}", p.len()); assert!(p.len() > 1); + assert!(m == "application/json") }); } @@ -87,8 +97,10 @@ fn test_fetch_sync_string() { fn test_fetch_sync_string_with_redirect() { // Relies on external http server. See tools/http_server.py tokio_util::init(|| { - let p = fetch_sync_string("http://127.0.0.1:4546/package.json").unwrap(); + let (p, m) = + fetch_sync_string("http://127.0.0.1:4546/package.json").unwrap(); println!("package.json len {}", p.len()); assert!(p.len() > 1); + assert!(m == "application/json") }); } diff --git a/src/msg.fbs b/src/msg.fbs index 9aa9b069c..870da1c40 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -102,6 +102,13 @@ table CwdRes { cwd: string; } +enum MediaType: byte { + JavaScript = 0, + TypeScript, + Json, + Unknown +} + table Base { cmd_id: uint32; sync: bool = true; // TODO(ry) Change default to false. @@ -137,6 +144,7 @@ table CodeFetchRes { // is the location of the locally downloaded source code. module_name: string; filename: string; + media_type: MediaType; source_code: string; output_code: string; // Non-empty only if cached. } diff --git a/src/ops.rs b/src/ops.rs index 4adea46f6..83a719a29 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -253,6 +253,7 @@ fn op_code_fetch( let mut msg_args = msg::CodeFetchResArgs { module_name: Some(builder.create_string(&out.module_name)), filename: Some(builder.create_string(&out.filename)), + media_type: out.media_type, source_code: Some(builder.create_string(&out.source_code)), ..Default::default() }; |