pull/12916/merge
Anton Zavodchikov 2022-04-14 22:10:26 +08:00 committed by GitHub
commit b7b9d05bd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 125 additions and 7 deletions

View File

@ -3,13 +3,39 @@ module vweb
import net.urllib import net.urllib
import net.http import net.http
fn parse_middleware(args []MethodArgs, attrs []string) ?string {
if 'use' in attrs {
for arg in args {
// ast.string_type_idx = 20
if arg.typ != 20 {
return error('middleware arguments should be string type, `$arg.name` does not meet that rule')
}
}
for attr in attrs {
if attr.starts_with('/') {
params_len := attr.split('/').filter(it != '' && it.starts_with(':')).len
if params_len != args.len {
return error('number of middleware arguments does not meet with number of path params')
}
return attr
}
}
return error("please specify path as attribute for middleware, example: ['/admin']")
}
return none
}
// Parsing function attributes for methods and path. // Parsing function attributes for methods and path.
fn parse_attrs(name string, attrs []string) ?([]http.Method, string) { fn parse_attrs(name string, attrs []string) ?([]http.Method, string) {
if attrs.len == 0 { if attrs.len == 0 {
return [http.Method.get], '/$name' return [http.Method.get], '/$name'
} }
mut x := attrs.clone() mut x := attrs.clone().filter(it.to_lower() != 'use')
mut methods := []http.Method{} mut methods := []http.Method{}
mut path := '' mut path := ''

View File

@ -399,7 +399,18 @@ pub fn run_at<T>(global_app &T, params RunParams) ? {
// Parsing methods attributes // Parsing methods attributes
mut routes := map[string]Route{} mut routes := map[string]Route{}
mut middlewares := map[string]string{}
$for method in T.methods { $for method in T.methods {
if middleware_path := parse_middleware(method.args, method.attrs) {
middlewares[method.name] = middleware_path
} else {
// There is no `use` attribute, so it returns `none`
if err.str() != 'none' {
eprintln('error parsing `app.$method.name` attributes: $err')
return
}
}
http_methods, route_path := parse_attrs(method.name, method.attrs) or { http_methods, route_path := parse_attrs(method.name, method.attrs) or {
return error('error parsing method attributes: $err') return error('error parsing method attributes: $err')
} }
@ -430,12 +441,12 @@ pub fn run_at<T>(global_app &T, params RunParams) ? {
eprintln('accept() failed with error: $err.msg()') eprintln('accept() failed with error: $err.msg()')
continue continue
} }
go handle_conn<T>(mut conn, mut request_app, routes) go handle_conn<T>(mut conn, mut request_app, routes, middlewares)
} }
} }
[manualfree] [manualfree]
fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) { fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route, middlewares map[string]string) {
conn.set_read_timeout(30 * time.second) conn.set_read_timeout(30 * time.second)
conn.set_write_timeout(30 * time.second) conn.set_write_timeout(30 * time.second)
defer { defer {
@ -527,15 +538,25 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) {
for param in method.args { for param in method.args {
args << form[param.name] args << form[param.name]
} }
fire_middlewares(mut app, url_words, middlewares)
if app.done == false {
app.$method(args) app.$method(args)
}
} else { } else {
fire_middlewares(mut app, url_words, middlewares)
if app.done == false {
app.$method() app.$method()
} }
}
return return
} }
if url_words.len == 0 && route_words == ['index'] && method.name == 'index' { if url_words.len == 0 && route_words == ['index'] && method.name == 'index' {
fire_middlewares(mut app, url_words, middlewares)
if app.done == false {
app.$method() app.$method()
}
return return
} }
@ -544,7 +565,10 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) {
if method_args.len != method.args.len { if method_args.len != method.args.len {
eprintln('warning: uneven parameters count ($method.args.len) in `$method.name`, compared to the vweb route `$method.attrs` ($method_args.len)') eprintln('warning: uneven parameters count ($method.args.len) in `$method.name`, compared to the vweb route `$method.attrs` ($method_args.len)')
} }
fire_middlewares(mut app, url_words, middlewares)
if app.done == false {
app.$method(method_args) app.$method(method_args)
}
return return
} }
} }
@ -554,6 +578,74 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) {
conn.write(vweb.http_404.bytes()) or {} conn.write(vweb.http_404.bytes()) or {}
} }
struct Firable_middleware {
method string
params []string
path_len int
}
fn fire_middlewares<T>(mut app T, url_words []string, middlewares map[string]string) {
mut fire_those := []Firable_middleware{}
for m, path in middlewares {
path_words := path.split('/').filter(it != '')
if path_words.len == 0 {
fire_those << Firable_middleware{
method: m
path_len: path_words.len
}
continue
}
if path_words.len > url_words.len {
continue
}
ext_words := url_words[..path_words.len]
if !path.contains('/:') && path_words == ext_words {
fire_those << Firable_middleware{
method: m
path_len: path_words.len
}
} else if path_words.last().starts_with(':') && path_words.last().ends_with('...') {
if params := route_matches(url_words, path_words) {
fire_those << Firable_middleware{
method: m
params: params
path_len: path_words.len
}
}
} else {
if params := route_matches(ext_words, path_words) {
fire_those << Firable_middleware{
method: m
params: params
path_len: path_words.len
}
}
}
}
fire_those.sort(a.path_len < b.path_len)
for f in fire_those {
fire_middleware(mut app, f.method, f.params)
}
}
fn fire_middleware<T>(mut app T, method_name string, params []string) {
$for method in T.methods {
if method_name == method.name {
if method.args.len > 0 && params.len == method.args.len {
app.$method(params)
} else {
app.$method()
}
}
}
}
fn route_matches(url_words []string, route_words []string) ?[]string { fn route_matches(url_words []string, route_words []string) ?[]string {
// URL path should be at least as long as the route path // URL path should be at least as long as the route path
// except for the catchall route (`/:path...`) // except for the catchall route (`/:path...`)