From 0efd083e7c843a290f54b4858f8b20722ec90b0c Mon Sep 17 00:00:00 2001 From: Anton Zavodchikov Date: Tue, 21 Dec 2021 07:09:54 +0500 Subject: [PATCH 1/8] ignore `use` attribute in parse --- vlib/vweb/parse.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vlib/vweb/parse.v b/vlib/vweb/parse.v index f28d7a1a0d..984af522cc 100644 --- a/vlib/vweb/parse.v +++ b/vlib/vweb/parse.v @@ -9,7 +9,7 @@ fn parse_attrs(name string, attrs []string) ?([]http.Method, string) { return [http.Method.get], '/$name' } - mut x := attrs.clone() + mut x := attrs.clone().filter(it.to_lower() != 'use') mut methods := []http.Method{} mut path := '' From 0a3a31fadb40b5fe7667d163fd4aa8d6e9b90ccf Mon Sep 17 00:00:00 2001 From: Anton Zavodchikov Date: Tue, 21 Dec 2021 07:10:57 +0500 Subject: [PATCH 2/8] App methods with `use` attribute is middlewares --- vlib/vweb/vweb.v | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index ce9b6affd7..74ee691339 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -383,15 +383,20 @@ pub fn run(global_app &T, port int) { // Parsing methods attributes mut routes := map[string]Route{} + mut middlewares := map[string]string{} $for method in T.methods { http_methods, route_path := parse_attrs(method.name, method.attrs) or { eprintln('error parsing method attributes: $err') return } - routes[method.name] = Route{ - methods: http_methods - path: route_path + if 'use' in method.attrs.filter(it.len == 3).map(it.to_lower()) { + middlewares[method.name] = route_path + } else { + routes[method.name] = Route{ + methods: http_methods + path: route_path + } } } println('[Vweb] Running app on http://localhost:$port') @@ -414,12 +419,12 @@ pub fn run(global_app &T, port int) { eprintln('accept() failed with error: $err.msg') continue } - go handle_conn(mut conn, mut request_app, routes) + go handle_conn(mut conn, mut request_app, routes, middlewares) } } [manualfree] -fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route) { +fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route, middlewares map[string]string) { conn.set_read_timeout(30 * time.second) conn.set_write_timeout(30 * time.second) defer { From ca377dd6e25d7fdd3643ed01f05ef9cd2e6836ca Mon Sep 17 00:00:00 2001 From: Anton Zavodchikov Date: Tue, 21 Dec 2021 07:12:14 +0500 Subject: [PATCH 3/8] Add stupid, simple impl of middlewares firing --- vlib/vweb/vweb.v | 66 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 74ee691339..06efbfd4f7 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -511,15 +511,25 @@ fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route, midd for param in method.args { args << form[param.name] } - app.$method(args) + + fire_middlewares(mut app, url_words, middlewares) + if app.done == false { + app.$method(args) + } } else { - app.$method() + fire_middlewares(mut app, url_words, middlewares) + if app.done == false { + app.$method() + } } return } if url_words.len == 0 && route_words == ['index'] && method.name == 'index' { - app.$method() + fire_middlewares(mut app, url_words, middlewares) + if app.done == false { + app.$method() + } return } @@ -528,7 +538,10 @@ fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route, midd 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)') } - app.$method(method_args) + fire_middlewares(mut app, url_words, middlewares) + if app.done == false { + app.$method(method_args) + } return } } @@ -538,6 +551,51 @@ fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route, midd conn.write(vweb.http_404.bytes()) or {} } +struct Firable_middleware { + method string + params []string + path_len int +} + +fn fire_middlewares(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 != '') + ext_words := url_words[..path_words.len] + + if (path_words.len == 0 && url_words.len == 0) || (!path.contains('/:') && path_words == ext_words) { + fire_those << Firable_middleware{ + method: m + 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(mut app T, method_name string, params []string) { + $for method in T.methods { + if method_name == method.name { + if params.len != method.args.len { + app.$method(params) + } else { + app.$method() + } + } + } +} + fn route_matches(url_words []string, route_words []string) ?[]string { // URL path should be at least as long as the route path // except for the catchall route (`/:path...`) From 748274b8781b1627bee88294f45044cba1d1e31d Mon Sep 17 00:00:00 2001 From: Anton Zavodchikov Date: Tue, 21 Dec 2021 08:50:42 +0500 Subject: [PATCH 4/8] parse_middleware with arguments check --- vlib/vweb/parse.v | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/vlib/vweb/parse.v b/vlib/vweb/parse.v index 984af522cc..6a78e99227 100644 --- a/vlib/vweb/parse.v +++ b/vlib/vweb/parse.v @@ -3,6 +3,32 @@ module vweb import net.urllib 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. fn parse_attrs(name string, attrs []string) ?([]http.Method, string) { if attrs.len == 0 { From 885d7dfbe9e6eeaf1a549fdbf71afcd1402d98cd Mon Sep 17 00:00:00 2001 From: Anton Zavodchikov Date: Tue, 21 Dec 2021 08:51:10 +0500 Subject: [PATCH 5/8] Parse middlewares at start --- vlib/vweb/vweb.v | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 06efbfd4f7..5f61ce4e40 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -385,18 +385,24 @@ pub fn run(global_app &T, port int) { mut routes := map[string]Route{} mut middlewares := map[string]string{} $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 { eprintln('error parsing method attributes: $err') return } - if 'use' in method.attrs.filter(it.len == 3).map(it.to_lower()) { - middlewares[method.name] = route_path - } else { - routes[method.name] = Route{ - methods: http_methods - path: route_path - } + routes[method.name] = Route{ + methods: http_methods + path: route_path } } println('[Vweb] Running app on http://localhost:$port') From 6a1e62528f960e40bbe909da261eae1b1ebe844a Mon Sep 17 00:00:00 2001 From: Anton Zavodchikov Date: Tue, 21 Dec 2021 08:52:12 +0500 Subject: [PATCH 6/8] fix: path matching, middleware firing args len --- vlib/vweb/vweb.v | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 5f61ce4e40..dcba160f36 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -567,23 +567,46 @@ fn fire_middlewares(mut app T, url_words []string, middlewares map[string]str mut fire_those := []Firable_middleware{} for m, path in middlewares { path_words := path.split('/').filter(it != '') - ext_words := url_words[..path_words.len] - if (path_words.len == 0 && url_words.len == 0) || (!path.contains('/:') && path_words == ext_words) { + if path_words.len == 0 { fire_those << Firable_middleware{ method: m path_len: path_words.len } - } else if params := route_matches(ext_words, path_words) { + 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 - params: params 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) + fire_those.sort(a.path_len > b.path_len) for f in fire_those { fire_middleware(mut app, f.method, f.params) @@ -593,7 +616,7 @@ fn fire_middlewares(mut app T, url_words []string, middlewares map[string]str fn fire_middleware(mut app T, method_name string, params []string) { $for method in T.methods { if method_name == method.name { - if params.len != method.args.len { + if method.args.len > 0 && params.len == method.args.len { app.$method(params) } else { app.$method() From e7e98b9180a2c7c06719e7945b865d0ed7108480 Mon Sep 17 00:00:00 2001 From: Anton Zavodchikov Date: Tue, 21 Dec 2021 09:13:58 +0500 Subject: [PATCH 7/8] fix: sort typo --- vlib/vweb/vweb.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index dcba160f36..79b660e308 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -606,7 +606,7 @@ fn fire_middlewares(mut app T, url_words []string, middlewares map[string]str } } - fire_those.sort(a.path_len > b.path_len) + fire_those.sort(a.path_len < b.path_len) for f in fire_those { fire_middleware(mut app, f.method, f.params) From 7fc3112c749cdfbf85081a15a8df1b7fa0f2a024 Mon Sep 17 00:00:00 2001 From: Anton Zavodchikov Date: Tue, 21 Dec 2021 09:18:16 +0500 Subject: [PATCH 8/8] run `fmt` over code --- vlib/vweb/parse.v | 2 +- vlib/vweb/vweb.v | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vlib/vweb/parse.v b/vlib/vweb/parse.v index 6a78e99227..85303eadeb 100644 --- a/vlib/vweb/parse.v +++ b/vlib/vweb/parse.v @@ -8,7 +8,7 @@ fn parse_middleware(args []MethodArgs, attrs []string) ?string { 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") + return error('middleware arguments should be string type, `$arg.name` does not meet that rule') } } diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 79b660e308..d829936c60 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -558,8 +558,8 @@ fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route, midd } struct Firable_middleware { - method string - params []string + method string + params []string path_len int }