///|
/// Native HTTP client using moonbitlang/async's @async_http.Client
///|
fn find_path_start(url : String) -> Int? {
// Skip protocol (http:// or https://)
let start = if url.has_prefix("https://") {
8
} else if url.has_prefix("http://") {
7
} else {
0
}
// Find the first slash after the host
for i = start; i < url.length(); i = i + 1 {
if url[i].to_int() == '/'.to_int() {
return Some(i)
}
}
None
}
///|
/// Extract host URL (without path) from full URL
/// e.g., "https://example.com/foo/bar" -> "https://example.com"
fn extract_host_url(url : String) -> String {
match find_path_start(url) {
Some(path_start) => url.unsafe_substring(start=0, end=path_start)
None => url
}
}
///|
/// Extract path from URL (e.g., "https://example.com/foo/bar" -> "/foo/bar")
fn extract_path_from_url(url : String) -> String {
match find_path_start(url) {
Some(path_start) => url.unsafe_substring(start=path_start, end=url.length())
None => "/" // No path, use root
}
}
///|
/// Fetch URL using native HTTP client
pub async fn fetch(
url : String,
options? : FetchOptions = FetchOptions::default(),
) -> HttpResponse raise HttpError {
let prepared = prepare_fetch_options(url, options)
// Extract host URL (without path) and path separately
let host_url = extract_host_url(url)
let path = extract_path_from_url(url)
// Create HTTP client with just the host
let client = @async_http.Client::new(host_url, headers=prepared.headers) catch {
_ => raise InvalidUrl(url)
}
// Perform request based on method
let response = try {
match prepared.http_method {
"GET" => client.get(path)
"POST" => client.post(path, prepared.body)
"PUT" => client.put(path, prepared.body)
_ => client.get(path)
}
} catch {
err => {
client.close()
raise NetworkError(err.to_string())
}
}
// Read response body using Reader trait method
let body_data = @async_io.Reader::read_all(client) catch {
err => {
client.close()
raise NetworkError(err.to_string())
}
}
let body = body_data.text() catch { _ => "" }
client.close()
// Convert headers
let headers : Map[String, String] = {}
for k, v in response.headers {
headers[k.to_lower()] = v
}
let result = { status: response.code, headers, body }
enforce_cors_response(url, prepared, result)
}