///|
/// Upgrade header parsing errors (RFC 9110 Section 7.8)
pub(all) enum UpgradeError {
Empty
InvalidFormat
InvalidProtocol
InvalidVersion
} derive(Show, Eq)
///|
/// Upgrade protocol
pub(all) struct Protocol {
name : String
version : String?
} derive(Show, Eq)
///|
pub fn Protocol::name(self : Protocol) -> String {
self.name
}
///|
pub fn Protocol::version(self : Protocol) -> String? {
self.version
}
///|
pub fn Protocol::to_string(self : Protocol) -> String {
match self.version {
Some(v) => self.name + "/" + v
None => self.name
}
}
///|
/// Upgrade header (RFC 9110 Section 7.8)
pub(all) struct Upgrade {
protocols : Array[Protocol]
} derive(Show, Eq)
///|
pub fn Upgrade::parse(input : String) -> Result[Upgrade, UpgradeError] {
let s = upgrade_trim_string(input)
if s.length() == 0 {
return Err(Empty)
}
let parts = upgrade_split_string(s, ",")
let protocols : Array[Protocol] = []
let mut idx = 0
while idx < parts.length() {
let part = upgrade_trim_string(parts[idx])
if part.length() == 0 {
return Err(InvalidFormat)
}
let parse_result = upgrade_parse_protocol(part)
match parse_result {
Err(e) => return Err(e)
Ok(p) => protocols.push(p)
}
idx = idx + 1
}
if protocols.length() == 0 {
return Err(Empty)
}
Ok({ protocols, })
}
///|
pub fn Upgrade::protocols(self : Upgrade) -> Array[Protocol] {
self.protocols
}
///|
pub fn Upgrade::has_protocol(self : Upgrade, protocol : String) -> Bool {
let mut idx = 0
while idx < self.protocols.length() {
let p = self.protocols[idx]
if p.name == upgrade_to_lower_case(protocol) {
return true
}
idx = idx + 1
}
false
}
///|
pub fn Upgrade::to_string(self : Upgrade) -> String {
let strs = upgrade_map_protocols(self.protocols, fn(p) { p.to_string() })
upgrade_join_strings(strs, ", ")
}
///|
/// Helper functions
///
///|
fn upgrade_parse_protocol(input : String) -> Result[Protocol, UpgradeError] {
let slash_pos = upgrade_find_char_in(input, 0, 47) // /
match slash_pos {
Some(pos) => {
let name = upgrade_trim_string(upgrade_string_slice(input, 0, pos))
let rest = upgrade_string_slice_from(input, pos + 1)
// Check for second slash (invalid)
if upgrade_find_char_in(rest, 0, 47) != None { // /
return Err(InvalidFormat)
}
let version = upgrade_trim_string(rest)
if name.length() == 0 {
return Err(InvalidProtocol)
}
if version.length() == 0 {
return Err(InvalidVersion)
}
if !upgrade_is_valid_token(name) {
return Err(InvalidProtocol)
}
if !upgrade_is_valid_token(version) {
return Err(InvalidVersion)
}
Ok({
name: upgrade_to_lower_case(name),
version: Some(upgrade_to_lower_case(version)),
})
}
None => {
if !upgrade_is_valid_token(input) {
return Err(InvalidProtocol)
}
Ok({ name: upgrade_to_lower_case(input), version: None })
}
}
}
///|
fn upgrade_is_valid_token(s : String) -> Bool {
if s.length() == 0 {
return false
}
let mut idx = 0
while idx < s.length() {
if !upgrade_is_token_char(upgrade_char_at(s, idx)) {
return false
}
idx = idx + 1
}
true
}
///|
fn upgrade_is_token_char(b : Int) -> Bool {
b == 33 ||
b == 35 ||
b == 36 ||
b == 37 ||
b == 38 ||
b == 39 ||
b == 42 ||
b == 43 ||
b == 45 ||
b == 46 ||
b == 48 ||
b == 49 ||
b == 50 ||
b == 51 ||
b == 52 ||
b == 53 ||
b == 54 ||
b == 55 ||
b == 56 ||
b == 57 ||
(b >= 65 && b <= 90) ||
b == 94 ||
b == 95 ||
b == 96 ||
(b >= 97 && b <= 122) ||
b == 124 ||
b == 126
}
///|
fn upgrade_char_at(s : String, idx : Int) -> Int {
str_char_at(s, idx)
}
///|
fn upgrade_to_lower_case(s : String) -> String {
str_to_lower_case(s)
}
///|
fn upgrade_find_char_in(s : String, start : Int, target : Int) -> Int? {
let mut idx = start
while idx < s.length() {
if upgrade_char_at(s, idx) == target {
return Some(idx)
}
idx = idx + 1
}
None
}
///|
fn upgrade_trim_string(s : String) -> String {
str_trim_string(s)
}
///|
fn upgrade_split_string(s : String, sep : String) -> Array[String] {
str_split_string(s, sep)
}
///|
fn upgrade_string_slice(s : String, start : Int, end : Int) -> String {
str_string_slice(s, start, end)
}
///|
fn upgrade_string_slice_from(s : String, start : Int) -> String {
str_string_slice_from(s, start)
}
///|
fn[T, U] upgrade_map_protocols(arr : Array[T], f : (T) -> U) -> Array[U] {
let result : Array[U] = []
let mut idx = 0
while idx < arr.length() {
result.push(f(arr[idx]))
idx = idx + 1
}
result
}
///|
fn upgrade_join_strings(strs : Array[String], sep : String) -> String {
let mut result = ""
let mut idx = 0
while idx < strs.length() {
if idx > 0 {
result = result + sep
}
result = result + strs[idx]
idx = idx + 1
}
result
}