feat(web): file() now handles HEAD requests

main
Jef Roosens 2022-08-12 15:08:05 +02:00
parent 3a73ea0632
commit e7b45bf251
Signed by untrusted user: Jef Roosens
GPG Key ID: B75D4F293C7052DB
2 changed files with 20 additions and 79 deletions

View File

@ -45,15 +45,6 @@ fn (mut app App) get_repo_file(repo string, arch string, filename string) web.Re
full_path = os.join_path(app.repo.repos_dir, repo, arch, filename, 'desc') full_path = os.join_path(app.repo.repos_dir, repo, arch, filename, 'desc')
} }
// Scuffed way to respond to HEAD requests
if app.req.method == http.Method.head {
if os.exists(full_path) {
return app.status(http.Status.ok)
}
return app.not_found()
}
return app.file(full_path) return app.file(full_path)
} }

View File

@ -25,10 +25,10 @@ pub mut:
conn &net.TcpConn conn &net.TcpConn
// Gives access to a shared logger object // Gives access to a shared logger object
logger shared log.Log logger shared log.Log
// REQUEST
// time.ticks() from start of web connection handle. // time.ticks() from start of web connection handle.
// You can use it to determine how much time is spent on your request. // You can use it to determine how much time is spent on your request.
page_gen_start i64 page_gen_start i64
// REQUEST
static_files map[string]string static_files map[string]string
static_mime_types map[string]string static_mime_types map[string]string
// Map containing query params for the route. // Map containing query params for the route.
@ -103,6 +103,12 @@ pub fn (mut ctx Context) send_response_header() ? {
ctx.send_string(resp.bytestr())? ctx.send_string(resp.bytestr())?
} }
// send is a convenience function for sending the HTTP response with an empty
// body.
pub fn (mut ctx Context) send() bool {
return ctx.send_response('')
}
// send_response constructs the resulting HTTP response with the given body // send_response constructs the resulting HTTP response with the given body
// string & sends it to the client. // string & sends it to the client.
pub fn (mut ctx Context) send_response(res string) bool { pub fn (mut ctx Context) send_response(res string) bool {
@ -144,67 +150,32 @@ pub fn (mut ctx Context) json<T>(status http.Status, j T) Result {
// file Response HTTP_OK with file as payload // file Response HTTP_OK with file as payload
// This function manually implements responses because it needs to stream the file contents // This function manually implements responses because it needs to stream the file contents
pub fn (mut ctx Context) file(f_path string) Result { pub fn (mut ctx Context) file(f_path string) Result {
// If the file doesn't exist, just respond with a 404
if !os.is_file(f_path) { if !os.is_file(f_path) {
return ctx.not_found() ctx.status = .not_found
ctx.send()
return Result{}
} }
// ext := os.file_ext(f_path) file_size := os.file_size(f_path)
// data := os.read_file(f_path) or { ctx.header.add(http.CommonHeader.content_length, file_size.str())
// eprint(err.msg())
// ctx.server_error(500)
// return Result{}
// }
// content_type := web.mime_types[ext]
// if content_type == '' {
// eprintln('no MIME type found for extension $ext')
// ctx.server_error(500)
// return Result{} // A HEAD request only returns the size of the file.
// } if ctx.req.method == .head {
ctx.send()
// First, we return the headers for the request return Result{}
}
// We open the file before sending the headers in case reading fails // We open the file before sending the headers in case reading fails
file_size := os.file_size(f_path)
mut file := os.open(f_path) or { mut file := os.open(f_path) or {
eprintln(err.msg()) eprintln(err.msg())
ctx.server_error(500) ctx.server_error(500)
return Result{} return Result{}
} }
// build header ctx.send_reader_response(mut file, file_size)
header := http.new_header_from_map({
// http.CommonHeader.content_type: content_type
http.CommonHeader.content_length: file_size.str()
}).join(ctx.header)
mut resp := http.Response{
header: header.join(headers_close)
}
resp.set_version(.v1_1)
resp.set_status(ctx.status)
ctx.send_string(resp.bytestr()) or { return Result{} }
ctx.send_reader(mut file, file_size) or { return Result{} }
// mut buf := []u8{len: 1_000_000}
// mut bytes_left := file_size
// // Repeat as long as the stream still has data
// for bytes_left > 0 {
// // TODO check if just breaking here is safe
// bytes_read := file.read(mut buf) or { break }
// bytes_left -= u64(bytes_read)
// mut to_write := bytes_read
// for to_write > 0 {
// // TODO don't just loop infinitely here
// bytes_written := ctx.conn.write(buf[bytes_read - to_write..bytes_read]) or { continue }
// to_write = to_write - bytes_written
// }
// }
return Result{} return Result{}
} }
@ -472,27 +443,6 @@ fn route_matches(url_words []string, route_words []string) ?[]string {
return params return params
} }
// ip Returns the ip address from the current user
pub fn (ctx &Context) ip() string {
mut ip := ctx.req.header.get(.x_forwarded_for) or { '' }
if ip == '' {
ip = ctx.req.header.get_custom('X-Real-Ip') or { '' }
}
if ip.contains(',') {
ip = ip.all_before(',')
}
if ip == '' {
ip = ctx.conn.peer_ip() or { '' }
}
return ip
}
// error Set s to the form error
pub fn (mut ctx Context) error(s string) {
println('web error: $s')
}
// filter Do not delete. // filter Do not delete.
// It used by `vlib/v/gen/c/str_intp.v:130` for string interpolation inside web templates // It used by `vlib/v/gen/c/str_intp.v:130` for string interpolation inside web templates
// TODO: move it to template render // TODO: move it to template render