///|
fn header(request : @core.Request, name : String) -> String? {
let wanted = name.to_lower()
for key, value in request.headers {
if key.to_lower() == wanted {
return Some(value)
}
}
None
}
///|
fn normalize_root_join(root : String, relative : String) -> String {
let norm_root = if root.length() > 1 &&
(root.has_suffix("/") || root.has_suffix("\\")) {
root[:root.length() - 1].to_owned()
} else {
root
}
if relative == "" {
norm_root
} else if relative.has_prefix("/") || relative.has_prefix("\\") {
norm_root + relative
} else {
norm_root + "/" + relative
}
}
///|
/// Escape characters for safe HTML rendering.
pub fn escape_html(s : String) -> String {
let buf = StringBuilder()
for c in s {
match c {
'&' => buf.write_string("&")
'<' => buf.write_string("<")
'>' => buf.write_string(">")
'"' => buf.write_string(""")
'\'' => buf.write_string("'")
_ => buf.write_char(c)
}
}
buf.to_string()
}
///|
fn encode_path_segment(s : String) -> String {
let bytes = @utf8.encode(s)
let buf = StringBuilder()
for b in bytes {
let cp = b.to_int()
if (cp >= 'A'.to_int() && cp <= 'Z'.to_int()) ||
(cp >= 'a'.to_int() && cp <= 'z'.to_int()) ||
(cp >= '0'.to_int() && cp <= '9'.to_int()) ||
cp == '-'.to_int() ||
cp == '_'.to_int() ||
cp == '.'.to_int() ||
cp == '~'.to_int() {
buf.write_char(Int::unsafe_to_char(cp))
} else {
let hex = "0123456789ABCDEF"
buf.write_char('%')
buf.write_char(hex[(cp >> 4) & 0xF].to_int().unsafe_to_char())
buf.write_char(hex[cp & 0xF].to_int().unsafe_to_char())
}
}
buf.to_string()
}
///|
fn check_accepts_encoding(accept_header : String, encoding : String) -> Bool {
let lower = accept_header.to_lower()
let parts = lower.split(",").to_array()
let wanted = encoding.to_lower()
let mut accepted = false
for p in parts {
let item = p.trim().to_owned()
if item.length() == 0 {
continue
}
let subparts = item.split(";").to_array()
let enc = subparts[0].trim().to_owned()
let mut q_zero = false
if subparts.length() > 1 {
for i = 1; i < subparts.length(); i = i + 1 {
let param = subparts[i].trim().to_owned()
if param == "q=0" ||
param == "q=0.0" ||
param == "q=0.00" ||
param == "q=0.000" {
q_zero = true
}
}
}
if q_zero {
if enc == wanted || enc == "*" {
return false
}
} else if enc == wanted ||
enc == "*" ||
(wanted == "gzip" && (enc == "compress" || enc == "deflate")) {
accepted = true
}
}
accepted
}
///|
async fn StaticEngine::has_gzip_magic(self : StaticEngine, path : String) -> Bool {
try {
let f = self.open_file(path)
defer f.close()
if f.size < 2L { return false }
let bytes = f.read(0L, 2)
bytes.length() == 2 && bytes[0] == b'\x1f' && bytes[1] == b'\x8b'
} catch {
@native.NativeError::Missing => false
e => raise e
}
}
///|
fn format_file_size(size : Int64) -> String {
if size < 1024L {
size.to_string() + "B"
} else if size < 1048576L {
let kb = size.to_double() / 1024.0
format_1dec(kb) + "k"
} else if size < 1073741824L {
let mb = size.to_double() / 1048576.0
format_1dec(mb) + "M"
} else {
let gb = size.to_double() / 1073741824.0
format_1dec(gb) + "G"
}
}
///|
fn format_1dec(val : Double) -> String {
let rounded = (val * 10.0 + 0.5).to_int64()
let whole = rounded / 10L
let frac = rounded % 10L
whole.to_string() + "." + frac.to_string()
}
///|
fn format_listing_timestamp(epoch_sec : Int64) -> String {
let days_since_epoch = (epoch_sec / 86400L).to_int()
let mut rem_sec = (epoch_sec % 86400L).to_int()
if rem_sec < 0 {
rem_sec = rem_sec + 86400
}
let hour = rem_sec / 3600
let minute = rem_sec % 3600 / 60
let months = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
"Dec",
]
let mut y = 1970
let mut d = days_since_epoch
if d >= 0 {
while true {
let leap = (y % 4 == 0 && y % 100 != 0) || y % 400 == 0
let days_in_year = if leap { 366 } else { 365 }
if d < days_in_year {
break
}
d = d - days_in_year
y = y + 1
}
} else {
while d < 0 {
y = y - 1
let leap = (y % 4 == 0 && y % 100 != 0) || y % 400 == 0
let days_in_year = if leap { 366 } else { 365 }
d = d + days_in_year
}
}
let leap = (y % 4 == 0 && y % 100 != 0) || y % 400 == 0
let month_days = if leap {
[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
} else {
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
}
let mut m = 0
while m < 12 {
if d < month_days[m] {
break
}
d = d - month_days[m]
m = m + 1
}
let day = d + 1
let pad_day = if day < 10 { "0" + day.to_string() } else { day.to_string() }
let pad_hour = if hour < 10 {
"0" + hour.to_string()
} else {
hour.to_string()
}
let pad_min = if minute < 10 {
"0" + minute.to_string()
} else {
minute.to_string()
}
"\{pad_day}-\{months[m]}-\{y} \{pad_hour}:\{pad_min}"
}
///|
priv struct ListingEntry {
name : String
is_dir : Bool
size : Int64
mtime_sec : Int64
mut is_companion : Bool
mut has_br : Bool
mut has_gz : Bool
}
///|
fn compare_entries(a : ListingEntry, b : ListingEntry) -> Int {
let cmp = a.name.to_lower().compare(b.name.to_lower())
if cmp != 0 {
cmp
} else {
a.name.compare(b.name)
}
}
///|
fn listing_header(title_path : String, _raw_query : String?) -> String {
let buf = StringBuilder()
let enc_title = escape_html(title_path)
buf.write_string("\n\n\n")
buf.write_string(" \n")
buf.write_string(
" \n",
)
buf.write_string(" Index of " + enc_title + "\n")
buf.write_string(" \n\n\n")
buf.write_string("Index of " + enc_title + "
\n")
buf.write_string("\n")
buf.to_string()
}
///|
fn listing_row(entry : ListingEntry, raw_query : String?) -> String {
let buf = StringBuilder()
let q_suffix = match raw_query { Some(q) if q.length() > 0 => "?" + escape_html(q); _ => "" }
if entry.is_companion {
return buf.to_string()
}
if entry.name == ".." {
buf.write_string("")
buf.write_string(" | ")
buf.write_string(" | ")
buf.write_string(
"../ | ",
)
buf.write_string("
\n")
return buf.to_string()
}
let encoded_name = encode_path_segment(entry.name)
let href = if entry.is_dir {
"./" + encoded_name + "/" + q_suffix
} else {
"./" + encoded_name
}
let display_name = escape_html(entry.name) +
(if entry.is_dir { "/" } else { "" })
let size_str = if entry.is_dir { "" } else { format_file_size(entry.size) }
let time_str = format_listing_timestamp(entry.mtime_sec)
buf.write_string("")
buf.write_string("| " + time_str + " | ")
buf.write_string(
"" + size_str + " | ",
)
buf.write_string(
"" +
display_name +
"",
)
if entry.has_br {
buf.write_string(
" [br]",
)
}
if entry.has_gz {
buf.write_string(
" [gz]",
)
}
buf.write_string(" |
\n")
buf.to_string()
}
///|
fn listing_footer(host : String) -> String {
let buf = StringBuilder()
buf.write_string("
\n")
buf.write_string(
"
http-server running @ " + escape_html(host) + "\n",
)
buf.write_string("")
buf.to_string()
}
///|
fn render_directory_html(title_path : String, entries : Array[ListingEntry], raw_query : String?, host : String) -> String {
let buf = StringBuilder()
buf.write_string(listing_header(title_path, raw_query))
for entry in entries { buf.write_string(listing_row(entry, raw_query)) }
buf.write_string(listing_footer(host))
buf.to_string()
}
///|
/// Pure HTML directory listing renderer for testing and embedding (AD-05).
pub fn render_directory_listing_html(
title_path : String,
entry_names : Array[(String, Bool)],
raw_query? : String?,
host? : String = "localhost",
) -> String {
let entries : Array[ListingEntry] = []
for item in entry_names {
let (name, is_dir) = item
entries.push({
name,
is_dir,
size: 0L,
mtime_sec: 0L,
is_companion: false,
has_br: false,
has_gz: false,
})
}
let q = match raw_query {
Some(opt) => opt
None => None
}
render_directory_html(title_path, entries, q, host)
}
///|
fn StaticEngine::make_terminal_404_response(
self : StaticEngine,
request : @core.Request,
) -> ResponsePlan {
let text = "File not found. :("
let body_bytes = @utf8.encode(text)
let headers : Map[String, String] = Map([
("Content-Type", "text/plain; charset=UTF-8"),
("Content-Length", body_bytes.length().to_string()),
])
@core.apply_security_headers(headers, self.config, request.meth)
let body = if request.meth is Head {
PlanBody::Empty
} else {
PlanBody::Bytes(body_bytes)
}
{ status: 404, headers, body, }
}
///|
async fn StaticEngine::make_404_response(
self : StaticEngine,
request : @core.Request,
) -> ResponsePlan {
let custom_404 = normalize_root_join(self.config.root, "404.html")
let has_404_html = try
self.exists(custom_404) && self.kind(custom_404) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if has_404_html {
let file = self.open_file(custom_404)
let size = file.size
let headers : Map[String, String] = Map([
("Content-Type", "text/html; charset=UTF-8"),
("Content-Length", size.to_string()),
])
@core.apply_security_headers(headers, self.config, request.meth)
let body = if request.meth is Head {
PlanBody::Empty
} else {
PlanBody::FileRegion(file, 0L, size)
}
if request.meth is Head { file.close() }
{ status: 404, headers, body, }
} else {
self.make_terminal_404_response(request)
}
}
///|
/// Resolve and read one static request.
async fn StaticEngine::plan_request(
self : StaticEngine,
request : @core.Request,
) -> PlanResult {
// 1. Evaluate security policies (Host whitelist, Basic Auth, OPTIONS preflight)
let sec_decision = @core.evaluate_security_policies(self.config, request)
match sec_decision {
Unauthorized(headers, body) => {
let b = if request.meth is Head {
PlanBody::Empty
} else {
PlanBody::Bytes(body)
}
headers["Content-Type"] = "text/plain; charset=UTF-8"
headers["Content-Length"] = body.length().to_string()
return Handled({ status: 401, headers, body: b, })
}
Forbidden(headers, body) => {
let b = if request.meth is Head {
PlanBody::Empty
} else {
PlanBody::Bytes(body)
}
headers["Content-Type"] = "text/plain; charset=UTF-8"
headers["Content-Length"] = body.length().to_string()
return Handled({ status: 403, headers, body: b, })
}
Preflight(headers) => {
headers["Content-Length"] = "0"
return Handled({ status: 204, headers, body: PlanBody::Empty, })
}
Proceed => ()
}
// 2. HTTP method validation
if request.meth is Other(_) {
return Next
}
// 2.1 Robots.txt interception per D-03 / C042.04
if self.config.robots {
let raw_target_parts = request.target.split("?").to_array()
if raw_target_parts[0] == "/robots.txt" {
let body_bytes = @utf8.encode("User-agent: *\nDisallow: /\n")
let headers : Map[String, String] = Map([
("Content-Type", "text/plain"),
("Content-Length", body_bytes.length().to_string()),
])
@core.apply_security_headers(headers, self.config, request.meth)
let body = if request.meth is Head {
PlanBody::Empty
} else {
PlanBody::Bytes(body_bytes)
}
return Handled({ status: 200, headers, body, })
}
}
// 3. Path resolution & root anchoring
let resolved = @core.resolve_path(
self.config.root,
self.config.base_url,
request.target,
)
let path = match resolved {
Ok(p) => p
Err(@core.PathError::OutsideBaseUrl(_)) => {
// D-04 & C042.21: 403 Forbidden with empty body
let headers : Map[String, String] = Map([("Content-Length", "0")])
@core.apply_security_headers(headers, self.config, request.meth)
return Handled({ status: 403, headers, body: PlanBody::Empty, })
}
Err(@core.PathError::TraversalForbidden(_)) => {
let body_bytes = @utf8.encode("Access denied")
let headers : Map[String, String] = Map([
("Content-Type", "text/plain; charset=UTF-8"),
("Content-Length", body_bytes.length().to_string()),
])
@core.apply_security_headers(headers, self.config, request.meth)
let body = if request.meth is Head {
PlanBody::Empty
} else {
PlanBody::Bytes(body_bytes)
}
return Handled({ status: 403, headers, body, })
}
Err(@core.PathError::MalformedUri(_)) => {
let body_bytes = @utf8.encode("Bad Request")
let headers : Map[String, String] = Map([
("Content-Type", "text/plain; charset=UTF-8"),
("Content-Length", body_bytes.length().to_string()),
])
@core.apply_security_headers(headers, self.config, request.meth)
let body = if request.meth is Head {
PlanBody::Empty
} else {
PlanBody::Bytes(body_bytes)
}
return Handled({ status: 400, headers, body, })
}
Err(@core.PathError::NotFound(_)) => {
if self.config.proxy is Some(_) || !self.config.handle_error {
return Next
}
return Handled(self.make_404_response(request))
}
}
let raw_target_parts = request.target.split("?").to_array()
let raw_path = raw_target_parts[0].to_owned()
let raw_query = if raw_target_parts.length() > 1 {
Some(raw_target_parts[1].to_owned())
} else {
None
}
// 4. Directory Handling
let is_dir = try
self.exists(path) && self.kind(path) is @fs.FileKind::Directory
catch {
@native.NativeError::Missing => false
e => raise e
}
if is_dir {
if !raw_path.has_suffix("/") {
// Directory request without trailing slash
if !self.config.auto_index && !self.config.show_dir {
// C025 suppression
return Handled(self.make_404_response(request))
}
let loc = @core.format_dir_redirect(raw_path, raw_query)
let headers : Map[String, String] = Map([
("Location", loc),
("Content-Type", "text/html; charset=UTF-8"),
("Content-Length", "0"),
])
@core.apply_security_headers(headers, self.config, request.meth)
return Handled({ status: 302, headers, body: PlanBody::Empty, })
}
// Directory request with trailing slash
let mut dir_file_chosen : (String, String, String?)? = None // (chosen_file, represented_path, content_encoding)
if self.config.auto_index {
let default_ext = self.config.default_ext.unwrap_or("html")
let index_candidate = path + "/index." + default_ext
let accept = header(request, "accept-encoding").unwrap_or("")
if self.config.brotli && check_accepts_encoding(accept, "br") {
let p = index_candidate + ".br"
let p_exists = try
self.exists(p) && self.kind(p) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if p_exists {
dir_file_chosen = Some((p, index_candidate, Some("br")))
}
}
if dir_file_chosen is None &&
self.config.gzip &&
check_accepts_encoding(accept, "gzip") {
let p = index_candidate + ".gz"
let p_exists = try
self.exists(p) && self.kind(p) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if p_exists && self.has_gzip_magic(p) {
dir_file_chosen = Some((p, index_candidate, Some("gzip")))
}
}
if dir_file_chosen is None {
let p_exists = try
self.exists(index_candidate) &&
self.kind(index_candidate) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if p_exists {
dir_file_chosen = Some((index_candidate, index_candidate, None))
}
}
}
match dir_file_chosen {
Some((chosen, represented, encoding)) =>
return self.serve_file(request, chosen, represented, encoding)
None => ()
}
// No index file found
if self.config.show_dir {
if !self.config.dir_overrides_404 && !self.config.has_fallback() {
let root_404 = normalize_root_join(self.config.root, "404.html")
let has_404 = try
self.exists(root_404) && self.kind(root_404) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if has_404 {
return Handled(self.make_404_response(request))
}
}
// D-06: admit all metadata before building a listing. An unconsumed
// listing retains its reservation, so slow clients cannot grow memory.
if request.meth is Head {
let headers : Map[String,String]=Map([("Content-Type","text/html; charset=UTF-8")])
@core.apply_security_headers(headers,self.config,request.meth)
return Handled({status:200,headers,body:Empty})
}
let mut metadata_charge=request.target.length()*32+16384
self.runtime.reserve(directory=true,metadata_charge)
let mut retained=false
defer { if !retained { self.runtime.release(directory=true,metadata_charge) } }
// Render HTML directory listing
let (dir_names,name_charge) = self.readdir(
path,
include_hidden=self.config.show_dotfiles,
)
metadata_charge += name_charge
let entries : Array[ListingEntry] = []
let name_map : Map[String, ListingEntry] = Map([])
for name in dir_names {
if !self.config.show_dotfiles && name.has_prefix(".") {
continue
}
let child_path = path + "/" + name
let child = self.open_file(child_path) catch {
@native.NativeError::Missing => continue
e => raise e
}
let is_child_dir = child.kind == 2
let size = if is_child_dir { 0L } else { child.size }
let mtime_sec = child.mtime
child.close()
let entry : ListingEntry = {
name,
is_dir: is_child_dir,
size,
mtime_sec,
is_companion: false,
has_br: false,
has_gz: false,
}
entries.push(entry)
name_map[name] = entry
}
// $O(N) companion matching
for entry in entries {
if !entry.is_dir {
if entry.name.has_suffix(".br") && entry.name.length() > 3 {
let base = entry.name[:entry.name.length() - 3].to_owned()
if name_map.contains(base) {
entry.is_companion = true
name_map[base].has_br = true
}
} else if entry.name.has_suffix(".gz") && entry.name.length() > 3 {
let base = entry.name[:entry.name.length() - 3].to_owned()
if name_map.contains(base) {
entry.is_companion = true
name_map[base].has_gz = true
}
}
}
}
// Partition and sort ($O(N \log N)$)
let dirs : Array[ListingEntry] = []
let files : Array[ListingEntry] = []
for entry in entries {
if entry.is_companion {
continue
}
if entry.is_dir {
dirs.push(entry)
} else {
files.push(entry)
}
}
dirs.sort_by(compare_entries)
files.sort_by(compare_entries)
let final_entries : Array[ListingEntry] = []
let norm_root = normalize_root_join(self.config.root, "")
let norm_path = normalize_root_join(path, "")
if norm_path != norm_root {
final_entries.push({
name: "..",
is_dir: true,
size: 0L,
mtime_sec: 0L,
is_companion: false,
has_br: false,
has_gz: false,
})
}
for d in dirs {
final_entries.push(d)
}
for f in files {
final_entries.push(f)
}
let host = header(request, "host").unwrap_or("localhost")
let display_title = if raw_path.has_suffix("/") {
raw_path
} else {
raw_path + "/"
}
let headers : Map[String, String] = Map([
("Content-Type", "text/html; charset=UTF-8"),
])
@core.apply_security_headers(headers, self.config, request.meth)
let body = if request.meth is Head {
PlanBody::Empty
} else {
PlanBody::Listing({ entries: final_entries, title: display_title, query: raw_query, host, stage: 0, index: 0, pending: b"", position: 0, metadata_charge })
}
retained=true
return Handled({ status: 200, headers, body, })
}
// Neither auto_index with existing index nor show_dir -> 404
if self.config.proxy is Some(_) || !self.config.handle_error {
return Next
}
return Handled(self.make_404_response(request))
}
// 5. Regular File Representation Selection & Pre-Compression
let mut file_candidate : (String, String, String?)? = None
let accept = header(request, "accept-encoding").unwrap_or("")
// 5a. forceContentEncoding directly requesting .br or .gz
if self.config.force_content_encoding {
if path.has_suffix(".br") && path.length() > 3 {
let exists = try
self.exists(path) && self.kind(path) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if exists {
let represented = path[:path.length() - 3].to_owned()
file_candidate = Some((path, represented, Some("br")))
}
} else if path.has_suffix(".gz") && path.length() > 3 {
let exists = try
self.exists(path) && self.kind(path) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if exists {
let represented = path[:path.length() - 3].to_owned()
file_candidate = Some((path, represented, Some("gzip")))
}
}
}
// 5b. Pre-compressed Brotli negotiation
if file_candidate is None &&
self.config.brotli &&
check_accepts_encoding(accept, "br") {
let p = path + ".br"
let exists = try
self.exists(p) && self.kind(p) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if exists {
file_candidate = Some((p, path, Some("br")))
}
}
// 5c. Pre-compressed Gzip negotiation
if file_candidate is None &&
self.config.gzip &&
check_accepts_encoding(accept, "gzip") {
let p = path + ".gz"
let exists = try
self.exists(p) && self.kind(p) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if exists && self.has_gzip_magic(p) {
file_candidate = Some((p, path, Some("gzip")))
}
}
// 5d. Direct regular file
if file_candidate is None {
let exists = try
self.exists(path) && self.kind(path) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if exists {
file_candidate = Some((path, path, None))
}
}
// 5e. Default extension completion
if file_candidate is None && self.config.default_ext is Some(ext) {
if !@core.has_file_extension(path) {
let p = path + "." + ext
let exists = try
self.exists(p) && self.kind(p) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if exists {
file_candidate = Some((p, p, None))
}
}
}
// 5f. If regular file found, serve it!
match file_candidate {
Some((chosen, represented, encoding)) =>
return self.serve_file(request, chosen, represented, encoding)
None => ()
}
// 6. SPA & Try-Files Fallback on 404 Condition
if self.config.has_fallback() {
let fallback_target = self.config
.fallback_target_file()
.unwrap_or("index.html")
let fallback_path = normalize_root_join(self.config.root, fallback_target)
let fallback_exists = try
self.exists(fallback_path) &&
self.kind(fallback_path) is @fs.FileKind::Regular
catch {
@native.NativeError::Missing => false
e => raise e
}
if fallback_exists {
return self.serve_file(request, fallback_path, fallback_target, None)
} else {
// Terminal 404: fallback file missing on disk (D-04 line 136)
return Handled(self.make_terminal_404_response(request))
}
}
if self.config.proxy is Some(_) || !self.config.handle_error {
return Next
}
Handled(self.make_404_response(request))
}
///|
/// Serve a resolved regular file representation with full RFC HTTP/1.1 features:
/// ETag, Last-Modified, Cache-Control, Range (206/416), 304 freshness, HEAD suppression, D-17 mutation detection.
async fn StaticEngine::serve_file(
self : StaticEngine,
request : @core.Request,
chosen : String,
represented_path : String,
content_encoding : String?,
) -> PlanResult {
let file = self.open_file(chosen)
errdefer file.close()
if file.kind != 1 { raise ServerError::FileChanged("chosen representation became a directory") }
if content_encoding == Some("gzip") {
let magic=if file.size >= 2L { file.read(0L,2) } else { b"" }
if magic.length()!=2 || magic[0]!=b'\x1f' || magic[1]!=b'\x8b' {
raise ServerError::FileChanged("chosen gzip representation changed before opening")
}
}
let file_size = file.size
let mtime_sec = file.mtime
let mime_type = @core.resolve_content_type(
represented_path,
self.mime_registry,
"application/octet-stream",
)
let server_etag = @core.EntityTag::from_metadata(
file_size,
mtime_sec,
self.config.weak_etags,
)
let etag_str = server_etag.to_header_value()
let last_modified = @core.format_http_date(mtime_sec)
let cache_control = self.config.effective_cache_control()
let headers : Map[String, String] = Map([
("Content-Type", mime_type),
("Cache-Control", cache_control),
("Accept-Ranges", "bytes"),
("ETag", etag_str),
("Last-Modified", last_modified),
])
match content_encoding {
Some(enc) => {
headers["Content-Encoding"] = enc
headers["Vary"] = "Accept-Encoding"
}
None => ()
}
let mut status = 200
let mut body : PlanBody = FileRegion(file, 0L, file_size)
// Range evaluation (RFC 7233)
if header(request, "if-range") is None && header(request, "range") is Some(range_header) {
let range_result = @core.parse_range_spec(range_header, file_size)
match range_result {
Satisfiable(r) => {
status = 206
let slice_len = r.end - r.start + 1L
headers["Content-Range"] = @core.format_content_range(
r.start,
r.end,
file_size,
)
headers["Content-Length"] = slice_len.to_string()
body = FileRegion(file, r.start, slice_len)
}
NotSatisfiable => {
status = 416
headers["Content-Range"] = @core.format_content_range_unsatisfiable(
file_size,
)
let msg = "Requested range not satisfiable"
let msg_bytes = @utf8.encode(msg)
headers["Content-Type"] = "text/plain; charset=UTF-8"
headers["Content-Length"] = msg_bytes.length().to_string()
body = Bytes(msg_bytes)
}
NotApplicable => headers["Content-Length"] = file_size.to_string()
}
} else {
headers["Content-Length"] = file_size.to_string()
}
// Conditional request evaluation (RFC 7232) -> 304 Not Modified
if status == 200 {
let inm = header(request, "if-none-match")
let ims = header(request, "if-modified-since")
if @core.should_return_304(
inm,
ims,
mtime_sec,
server_etag,
self.config.weak_compare,
) {
let fresh_headers : Map[String, String] = Map([
("ETag", etag_str),
("Last-Modified", last_modified),
("Cache-Control", cache_control),
("Content-Length", "0"),
])
match content_encoding {
Some(_) => fresh_headers["Vary"] = "Accept-Encoding"
None => ()
}
@core.apply_security_headers(fresh_headers, self.config, request.meth)
file.close()
return Handled({
status: 304,
headers: fresh_headers,
body: PlanBody::Empty,
})
}
}
// HEAD method suppresses body
if request.meth is Head {
body = PlanBody::Empty
}
// D-17 In-Flight Mutation Check
file.check()
if !(body is FileRegion(_, _, _)) { file.close() }
// Apply configured security headers
@core.apply_security_headers(headers, self.config, request.meth)
Handled({ status, headers, body, })
}