Repository: thecoshman/http (language: rust)

Issue:
Feature request: Specify headers
I'd like to be able to do `httplz . --header "Access-Control-Allow-Origin: *"` for example


Discussion / hints:

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index dde23da..f2ed2f1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,7 +9,7 @@ categories = ["network-programming", "web-programming::http-server"]
 license = "MIT"
 build = "build.rs"
 # Remember to also update in appveyor.yml
-version = "1.11.2"
+version = "1.12.0"
 # Remember to also update in http.md
 authors = ["thecoshman <rust@thecoshman.com>",
            "nabijaczleweli <nabijaczleweli@nabijaczleweli.xyz>",
diff --git a/appveyor.yml b/appveyor.yml
index 0904d17..5ce5104 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 1.11.2-{build}
+version: 1.12.0-{build}
 
 skip_tags: false
 
@@ -33,21 +33,21 @@ build: off
 build_script:
   - git submodule update --init --recursive
   - cargo build --verbose --release
-  - cp target\release\http.exe http-v1.11.2.exe
-  - strip --strip-all --remove-section=.comment --remove-section=.note http-v1.11.2.exe
-  - makensis -DHTTP_VERSION=v1.11.2 install.nsi
+  - cp target\release\http.exe http-v1.12.0.exe
+  - strip --strip-all --remove-section=.comment --remove-section=.note http-v1.12.0.exe
+  - makensis -DHTTP_VERSION=v1.12.0 install.nsi
 
 test: off
 test_script:
   - cargo test --verbose --release
 
 artifacts:
-  - path: http-v1.11.2.exe
-  - path: http v1.11.2 installer.exe
+  - path: http-v1.12.0.exe
+  - path: http v1.12.0 installer.exe
 
 deploy:
   provider: GitHub
-  artifact: /http.*v1.11.2.*\.exe/
+  artifact: /http.*v1.12.0.*\.exe/
   auth_token:
     secure: ZTXvCrv9y01s7Hd60w8W7NaouPnPoaw9YJt9WhWQ2Pep8HLvCikt9Exjkz8SGP9P
   on:
diff --git a/http.md b/http.md
index 4ddd306..3280e9a 100644
--- a/http.md
+++ b/http.md
@@ -146,6 +146,12 @@ pass parameters like what port to use.
 
     Default: 0.
 
+  -H --header [NAME: VALUE]...
+
+    Add the specified headers to all resposes.
+
+    VALUE is left-trimmed.
+
   -s --no-follow-symlinks
 
     Don't follow symlinks when requesting file access.
diff --git a/src/ops/mod.rs b/src/ops/mod.rs
index a60f96b..f98bed4 100644
--- a/src/ops/mod.rs
+++ b/src/ops/mod.rs
@@ -128,6 +128,7 @@ pub struct HttpHandler {
     pub encoded_temp_dir: Option<(String, PathBuf)>,
     pub proxies: BTreeMap<IpCidr, String>,
     pub mime_type_overrides: BTreeMap<String, Mime>,
+    pub additional_headers: Vec<(String, Vec<u8>)>,
     cache_gen: RwLock<CacheT<Vec<u8>>>,
     cache_fs: RwLock<CacheT<(PathBuf, bool)>>,
 }
@@ -168,6 +169,7 @@ impl HttpHandler {
             cache_fs: Default::default(),
             proxies: opts.proxies.clone(),
             mime_type_overrides: opts.mime_type_overrides.clone(),
+            additional_headers: opts.additional_headers.clone(),
         }
     }
 
@@ -238,6 +240,9 @@ impl Handler for HttpHandler {
         if self.webdav {
             resp.headers.set(Dav::LEVEL_1);
         }
+        for (h, v) in &self.additional_headers {
+            resp.headers.append_raw(h.clone(), v.clone());
+        }
         Ok(resp)
     }
 }
@@ -1346,6 +1351,7 @@ impl Clone for HttpHandler {
             encoded_temp_dir: self.encoded_temp_dir.clone(),
             proxies: self.proxies.clone(),
             mime_type_overrides: self.mime_type_overrides.clone(),
+            additional_headers: self.additional_headers.clone(),
             cache_gen: Default::default(),
             cache_fs: Default::default(),
         }
diff --git a/src/options.rs b/src/options.rs
index 282c3b2..40a3687 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -27,8 +27,9 @@ use std::fs;
 
 
 lazy_static! {
-    static ref CREDENTIALS_REGEX: Regex = Regex::new("[^:]+(?::[^:]+)?").unwrap();
-    static ref PATH_CREDENTIALS_REGEX: Regex = Regex::new("(.+)=([^:]+(?::[^:]+)?)?").unwrap();
+    static ref CREDENTIALS_REGEX: Regex = Regex::new("^[^:]+(?::[^:]+)?$").unwrap();
+    static ref PATH_CREDENTIALS_REGEX: Regex = Regex::new("^(.+)=([^:]+(?::[^:]+)?)?$").unwrap();
+    static ref HEADER_REGEX: Regex = Regex::new("^([^:]+):[[:space:]]*(.+)$").unwrap();
 }
 
 
@@ -110,6 +111,8 @@ pub struct Options {
     pub mime_type_overrides: BTreeMap<String, Mime>,
     /// Max amount of data per second each request is allowed to return. Default: `None`
     pub request_bandwidth: Option<NonZeroU64>,
+    /// Additional headers to add to every response
+    pub additional_headers: Vec<(String, Vec<u8>)>,
 }
 
 impl Options {
@@ -151,6 +154,7 @@ impl Options {
                 .validator(|s| Options::mime_type_override_parse(s.into()).map(|_| ())))
             .arg(Arg::from_usage("--request-bandwidth [BYTES] 'Limit each request to returning BYTES per second, or 0 for unlimited. Default: 0'")
                 .validator(|s| Options::bandwidth_parse(s.into()).map(|_| ())))
+            .arg(Arg::from_usage("-H --header [NAME: VALUE]... 'Headers to add to every response'").validator(|s| Options::header_parse(&s).map(|_| ())))
             .get_matches();
 
         let dir = matches.value_of("DIR").unwrap_or(".");
@@ -233,6 +237,11 @@ impl Options {
                 .map(Result::unwrap)
                 .collect(),
             request_bandwidth: matches.value_of("request-bandwidth").map(Cow::from).map(Options::bandwidth_parse).map(Result::unwrap).unwrap_or_default(),
+            additional_headers: matches.values_of("header")
+                .unwrap_or_default()
+                .map(Options::header_parse)
+                .map(Result::unwrap)
+                .collect(),
         }
     }
 
@@ -375,4 +384,8 @@ impl Options {
             }
         }
     }
+
+    fn header_parse(s: &str) -> Result<(String, Vec<u8>), String> {
+        HEADER_REGEX.captures(s).map(|hdr| (hdr[1].to_string(), hdr[2].as_bytes().to_vec())).ok_or_else(|| format!("\"{}\" invalid format", s))
+    }
 }


Test patch: