///|
pub struct HarBucket {
label_value : String
count_value : Int
bytes_value : Double
duration_value : Double
} derive(Eq, Debug)
///|
pub fn HarBucket::label(self : HarBucket) -> String {
self.label_value
}
///|
pub fn HarBucket::count(self : HarBucket) -> Int {
self.count_value
}
///|
pub fn HarBucket::bytes(self : HarBucket) -> Double {
self.bytes_value
}
///|
pub fn HarBucket::duration(self : HarBucket) -> Double {
self.duration_value
}
///|
pub fn HarBucket::average_duration(self : HarBucket) -> Double {
if self.count_value == 0 {
0.0
} else {
self.duration_value / self.count_value.to_double()
}
}
///|
priv struct MutableBucket {
label_value : String
mut count_value : Int
mut bytes_value : Double
mut duration_value : Double
}
///|
fn add_bucket(
buckets : Array[MutableBucket],
label : String,
bytes : Double,
duration : Double,
) -> Unit {
for bucket in buckets {
if bucket.label_value == label {
bucket.count_value += 1
bucket.bytes_value += bytes
bucket.duration_value += duration
return
}
}
buckets.push({
label_value: label,
count_value: 1,
bytes_value: bytes,
duration_value: duration,
})
}
///|
fn freeze_buckets(buckets : Array[MutableBucket]) -> Array[HarBucket] {
let result : Array[HarBucket] = []
for bucket in buckets {
result.push({
label_value: bucket.label_value,
count_value: bucket.count_value,
bytes_value: bucket.bytes_value,
duration_value: bucket.duration_value,
})
}
result
}
///|
fn owned_parts(value : String, separator : String) -> Array[String] {
value.split(separator).map(part => part.to_owned()).collect()
}
///|
fn last_string(values : Array[String]) -> String {
if values.is_empty() {
""
} else {
values[values.length() - 1]
}
}
///|
pub fn url_host(url : String) -> String {
let scheme_parts = owned_parts(url, "://")
let remainder = if scheme_parts.length() >= 2 {
scheme_parts[1]
} else {
scheme_parts[0]
}
let authority_parts = owned_parts(remainder, "/")
let authority = if authority_parts.is_empty() {
""
} else {
authority_parts[0]
}
let user_parts = owned_parts(authority, "@")
let host_port = last_string(user_parts)
if host_port.has_prefix("[") {
let bracket_parts = owned_parts(host_port, "]")
if bracket_parts.is_empty() {
host_port
} else {
bracket_parts[0] + "]"
}
} else {
let colon_parts = owned_parts(host_port, ":")
if colon_parts.is_empty() {
""
} else {
colon_parts[0].to_lower()
}
}
}
///|
pub fn url_scheme(url : String) -> String {
let parts = owned_parts(url, "://")
if parts.length() >= 2 {
parts[0].to_lower()
} else {
""
}
}
///|
pub struct HarStatistics {
entry_count_value : Int
page_count_value : Int
successful_count_value : Int
redirect_count_value : Int
error_count_value : Int
secure_count_value : Int
cached_count_value : Int
unknown_body_size_count_value : Int
request_bytes_value : Double
response_bytes_value : Double
decoded_bytes_value : Double
duration_value : Double
hosts_value : Array[HarBucket]
status_classes_value : Array[HarBucket]
content_types_value : Array[HarBucket]
} derive(Eq, Debug)
///|
pub fn HarStatistics::entry_count(self : HarStatistics) -> Int {
self.entry_count_value
}
///|
pub fn HarStatistics::page_count(self : HarStatistics) -> Int {
self.page_count_value
}
///|
pub fn HarStatistics::successful_count(self : HarStatistics) -> Int {
self.successful_count_value
}
///|
pub fn HarStatistics::redirect_count(self : HarStatistics) -> Int {
self.redirect_count_value
}
///|
pub fn HarStatistics::error_count(self : HarStatistics) -> Int {
self.error_count_value
}
///|
pub fn HarStatistics::secure_count(self : HarStatistics) -> Int {
self.secure_count_value
}
///|
pub fn HarStatistics::cached_count(self : HarStatistics) -> Int {
self.cached_count_value
}
///|
pub fn HarStatistics::unknown_body_size_count(self : HarStatistics) -> Int {
self.unknown_body_size_count_value
}
///|
pub fn HarStatistics::request_bytes(self : HarStatistics) -> Double {
self.request_bytes_value
}
///|
pub fn HarStatistics::response_bytes(self : HarStatistics) -> Double {
self.response_bytes_value
}
///|
pub fn HarStatistics::decoded_bytes(self : HarStatistics) -> Double {
self.decoded_bytes_value
}
///|
pub fn HarStatistics::duration(self : HarStatistics) -> Double {
self.duration_value
}
///|
pub fn HarStatistics::hosts(self : HarStatistics) -> Array[HarBucket] {
self.hosts_value.copy()
}
///|
pub fn HarStatistics::status_classes(self : HarStatistics) -> Array[HarBucket] {
self.status_classes_value.copy()
}
///|
pub fn HarStatistics::content_types(self : HarStatistics) -> Array[HarBucket] {
self.content_types_value.copy()
}
///|
pub fn HarStatistics::average_duration(self : HarStatistics) -> Double {
if self.entry_count_value == 0 {
0.0
} else {
self.duration_value / self.entry_count_value.to_double()
}
}
///|
pub fn HarStatistics::cache_ratio(self : HarStatistics) -> Double {
if self.entry_count_value == 0 {
0.0
} else {
self.cached_count_value.to_double() / self.entry_count_value.to_double()
}
}
///|
pub fn HarStatistics::secure_ratio(self : HarStatistics) -> Double {
if self.entry_count_value == 0 {
0.0
} else {
self.secure_count_value.to_double() / self.entry_count_value.to_double()
}
}
///|
pub fn HarStatistics::find_host(
self : HarStatistics,
host : String,
) -> HarBucket? {
for bucket in self.hosts_value {
if bucket.label_value == host.to_lower() {
return Some(bucket)
}
}
None
}
///|
pub fn HarStatistics::find_status_class(
self : HarStatistics,
status_class : String,
) -> HarBucket? {
for bucket in self.status_classes_value {
if bucket.label_value == status_class {
return Some(bucket)
}
}
None
}
///|
pub fn HarStatistics::find_content_type(
self : HarStatistics,
content_type : String,
) -> HarBucket? {
for bucket in self.content_types_value {
if bucket.label_value == content_type {
return Some(bucket)
}
}
None
}
///|
pub fn analyze_har(document : HarDocument) -> HarStatistics {
let mut successful = 0
let mut redirects = 0
let mut errors = 0
let mut secure = 0
let mut cached = 0
let mut unknown_body = 0
let mut request_bytes = 0.0
let mut response_bytes = 0.0
let mut decoded_bytes = 0.0
let mut duration = 0.0
let hosts : Array[MutableBucket] = []
let statuses : Array[MutableBucket] = []
let content_types : Array[MutableBucket] = []
for entry in document.entries_value {
let request = entry.request_value
let response = entry.response_value
if response.is_success() {
successful += 1
} else if response.is_redirect() {
redirects += 1
} else if response.is_error() {
errors += 1
}
if request.is_secure() {
secure += 1
}
if entry.cache_value.was_cached() {
cached += 1
}
match request.known_headers_size() {
Some(value) => request_bytes += value
None => ()
}
match request.known_body_size() {
Some(value) => request_bytes += value
None => ()
}
let transferred = match response.known_body_size() {
Some(value) => {
response_bytes += value
value
}
None => {
unknown_body += 1
0.0
}
}
match response.known_headers_size() {
Some(value) => response_bytes += value
None => ()
}
match response.content_value.known_size() {
Some(value) => decoded_bytes += value
None => ()
}
duration += entry.time_value
let host = url_host(request.url_value)
add_bucket(
hosts,
if host == "" {
"(unknown)"
} else {
host
},
transferred,
entry.time_value,
)
add_bucket(statuses, response.status_class(), transferred, entry.time_value)
add_bucket(
content_types,
response.content_value.type_group(),
transferred,
entry.time_value,
)
}
{
entry_count_value: document.entry_count(),
page_count_value: document.page_count(),
successful_count_value: successful,
redirect_count_value: redirects,
error_count_value: errors,
secure_count_value: secure,
cached_count_value: cached,
unknown_body_size_count_value: unknown_body,
request_bytes_value: request_bytes,
response_bytes_value: response_bytes,
decoded_bytes_value: decoded_bytes,
duration_value: duration,
hosts_value: freeze_buckets(hosts),
status_classes_value: freeze_buckets(statuses),
content_types_value: freeze_buckets(content_types),
}
}