Skip to content

Commit 25075fb

Browse files
authored
Merge pull request #75 from w-henderson/percent-encoding
Added percent-encoding support
2 parents f138e5a + 0c608be commit 25075fb

File tree

7 files changed

+117
-3
lines changed

7 files changed

+117
-3
lines changed

humphrey-server/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "humphrey_server"
3-
version = "0.5.0"
3+
version = "0.5.1"
44
edition = "2018"
55
license = "MIT"
66
homepage = "https://github.com/w-henderson/Humphrey"
@@ -11,7 +11,7 @@ keywords = ["http", "server", "http-server"]
1111
categories = ["web-programming::http-server", "network-programming", "command-line-utilities"]
1212

1313
[dependencies]
14-
humphrey = { version = "^0.5", path = "../humphrey" }
14+
humphrey = { version = "^0.5.2", path = "../humphrey" }
1515
libloading = { version = "0.7", optional = true }
1616

1717
[features]

humphrey/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "humphrey"
3-
version = "0.5.1"
3+
version = "0.5.2"
44
edition = "2018"
55
license = "MIT"
66
homepage = "https://github.com/w-henderson/Humphrey"

humphrey/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod handlers;
99
pub mod http;
1010
pub mod krauss;
1111
pub mod monitor;
12+
pub mod percent;
1213
pub mod route;
1314
pub mod stream;
1415
pub mod thread;

humphrey/src/percent.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//! Provides percent-encoding functionality.
2+
3+
const UNRESERVED_CHARACTERS: &[u8] =
4+
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~";
5+
6+
/// A trait which represents the ability of a type to be percent-encoded.
7+
pub trait PercentEncode {
8+
/// Percent-encode the value.
9+
fn percent_encode(&self) -> String;
10+
}
11+
12+
/// A trait which represents the ability of a type to be percent-decoded.
13+
pub trait PercentDecode {
14+
/// Attempt to percent-decode the value.
15+
fn percent_decode(&self) -> Option<Vec<u8>>;
16+
}
17+
18+
impl<T> PercentEncode for T
19+
where
20+
T: AsRef<[u8]>,
21+
{
22+
fn percent_encode(&self) -> String {
23+
let bytes = self.as_ref();
24+
let mut encoded = String::with_capacity(bytes.len() * 3);
25+
26+
for byte in bytes {
27+
if UNRESERVED_CHARACTERS.contains(byte) {
28+
encoded.push(*byte as char);
29+
} else {
30+
encoded += &format!("%{:02X}", byte);
31+
}
32+
}
33+
34+
encoded
35+
}
36+
}
37+
38+
impl<T> PercentDecode for T
39+
where
40+
T: AsRef<str>,
41+
{
42+
fn percent_decode(&self) -> Option<Vec<u8>> {
43+
let length = self.as_ref().len();
44+
let mut chars = self.as_ref().bytes();
45+
let mut decoded = Vec::with_capacity(length);
46+
47+
while let Some(character) = chars.next() {
48+
if character == b'%' {
49+
let [hex_dig_1, hex_dig_2] = [chars.next()?, chars.next()?];
50+
let hex = format!("{}{}", hex_dig_1 as char, hex_dig_2 as char);
51+
let byte = u8::from_str_radix(&hex, 16).ok()?;
52+
decoded.push(byte);
53+
} else {
54+
decoded.push(character);
55+
}
56+
}
57+
58+
Some(decoded)
59+
}
60+
}

humphrey/src/route.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::app::{
44
PathAwareRequestHandler, RequestHandler, StatelessRequestHandler, WebsocketHandler,
55
};
66
use crate::krauss;
7+
use crate::percent::PercentDecode;
78

89
use std::fs::metadata;
910
use std::path::PathBuf;
@@ -138,6 +139,8 @@ pub fn try_find_path(
138139
request_path: &str,
139140
index_files: &[&str],
140141
) -> Option<LocatedPath> {
142+
let request_path = String::from_utf8(request_path.percent_decode()?).ok()?;
143+
141144
// Avoid path traversal exploits
142145
if request_path.contains("..") || request_path.contains(':') {
143146
return None;

humphrey/src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod date;
22
pub mod krauss;
33
pub mod method;
44
pub mod mock_stream;
5+
pub mod percent;
56
pub mod request;
67
pub mod response;
78
pub mod status;

humphrey/src/tests/percent.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use crate::percent::{PercentDecode, PercentEncode};
2+
3+
#[test]
4+
fn encode_unreserved_chars() {
5+
let string = "thisisatest";
6+
let encoded = string.percent_encode();
7+
8+
assert_eq!(encoded, string);
9+
}
10+
11+
#[test]
12+
fn encode_reserved_chars() {
13+
let string = "this is a test! (and brackets)";
14+
let encoded = string.percent_encode();
15+
16+
assert_eq!(encoded, "this%20is%20a%20test%21%20%28and%20brackets%29");
17+
}
18+
19+
#[test]
20+
fn encode_bytes() {
21+
let bytes = b"this is a \0null character";
22+
let encoded = bytes.percent_encode();
23+
24+
assert_eq!(encoded, "this%20is%20a%20%00null%20character");
25+
}
26+
27+
#[test]
28+
fn decode_unreserved_chars() {
29+
let string = "thisisatest";
30+
let decoded = string.percent_decode();
31+
32+
assert_eq!(decoded, Some(string.as_bytes().to_vec()));
33+
}
34+
35+
#[test]
36+
fn decode_reserved_chars() {
37+
let string = "this%20is%20a%20test%21%20%28and%20brackets%29";
38+
let decoded = string.percent_decode();
39+
40+
assert_eq!(decoded, Some(b"this is a test! (and brackets)".to_vec()));
41+
}
42+
43+
#[test]
44+
fn decode_bytes() {
45+
let string = "this%20is%20a%20%00null%20character";
46+
let decoded = string.percent_decode();
47+
48+
assert_eq!(decoded, Some(b"this is a \0null character".to_vec()));
49+
}

0 commit comments

Comments
 (0)