Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2019-2026 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module s3
|
||||
|
||||
// uri_encode performs RFC 3986 percent-encoding as required by Signature V4.
|
||||
// Only the unreserved set A–Z / a–z / 0–9 / '-' / '_' / '.' / '~' is preserved.
|
||||
// When `encode_slash` is false (used for object keys), '/' is left intact and
|
||||
// backslashes are normalized to '/' so Windows-style paths produce the same
|
||||
// canonical key. All other bytes are emitted as %XX with uppercase hex digits.
|
||||
pub fn uri_encode(input string, encode_slash bool) string {
|
||||
mut out := []u8{cap: input.len + (input.len >> 2)} // 25% headroom
|
||||
for b in input.bytes() {
|
||||
match b {
|
||||
`A`...`Z`, `a`...`z`, `0`...`9`, `-`, `_`, `.`, `~` {
|
||||
out << b
|
||||
}
|
||||
`/`, `\\` {
|
||||
if encode_slash {
|
||||
append_percent(mut out, b)
|
||||
} else {
|
||||
out << if b == `\\` { u8(`/`) } else { b }
|
||||
}
|
||||
}
|
||||
else {
|
||||
append_percent(mut out, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.bytestr()
|
||||
}
|
||||
|
||||
// uri_encode_path encodes an S3 object key segment-aware: '/' is preserved
|
||||
// because S3 paths use it as the segment separator.
|
||||
@[inline]
|
||||
pub fn uri_encode_path(path string) string {
|
||||
return uri_encode(path, false)
|
||||
}
|
||||
|
||||
// uri_encode_query encodes a value that will appear inside a query string.
|
||||
// Slashes must be percent-encoded.
|
||||
@[inline]
|
||||
pub fn uri_encode_query(value string) string {
|
||||
return uri_encode(value, true)
|
||||
}
|
||||
|
||||
// strip_slashes removes leading and trailing '/' or '\\' separators. S3
|
||||
// canonical paths must contain a single leading slash, no trailing one.
|
||||
pub fn strip_slashes(s string) string {
|
||||
if s == '' {
|
||||
return s
|
||||
}
|
||||
mut start := 0
|
||||
mut end := s.len
|
||||
for start < end && (s[start] == `/` || s[start] == `\\`) {
|
||||
start++
|
||||
}
|
||||
for end > start && (s[end - 1] == `/` || s[end - 1] == `\\`) {
|
||||
end--
|
||||
}
|
||||
return s[start..end]
|
||||
}
|
||||
|
||||
// contains_crlf returns true if `value` contains a CR or LF byte. Header
|
||||
// values that pass user-provided strings (ACL, content-type, …) MUST be
|
||||
// checked to prevent HTTP header injection (CRLF smuggling).
|
||||
@[inline]
|
||||
pub fn contains_crlf(value string) bool {
|
||||
for b in value.bytes() {
|
||||
if b == `\r` || b == `\n` {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// to_hex_lower formats raw bytes as their lowercase hex string. Used for
|
||||
// SHA-256 digests inside SigV4 (the spec requires lowercase).
|
||||
pub fn to_hex_lower(data []u8) string {
|
||||
mut out := []u8{len: data.len * 2}
|
||||
for i, b in data {
|
||||
out[i * 2] = hex_lower_nibble(b >> 4)
|
||||
out[i * 2 + 1] = hex_lower_nibble(b & 0x0F)
|
||||
}
|
||||
return out.bytestr()
|
||||
}
|
||||
|
||||
@[inline]
|
||||
fn append_percent(mut out []u8, b u8) {
|
||||
out << `%`
|
||||
out << hex_upper_nibble(b >> 4)
|
||||
out << hex_upper_nibble(b & 0x0F)
|
||||
}
|
||||
|
||||
@[inline]
|
||||
fn hex_upper_nibble(n u8) u8 {
|
||||
return if n < 10 { n + `0` } else { n - 10 + `A` }
|
||||
}
|
||||
|
||||
@[inline]
|
||||
fn hex_lower_nibble(n u8) u8 {
|
||||
return if n < 10 { n + `0` } else { n - 10 + `a` }
|
||||
}
|
||||
Reference in new issue
Block a user