v2 / vlib / veb / parse.v
117 lines · 108 sloc · 2.83 KB · 07ee934f822c7a2980e0dc31ca635a6c734dcbca
Raw
1module veb
2
3import net.urllib
4import net.http
5
6// Parsing function attributes for methods and path.
7fn parse_attrs(name string, attrs []string) !([]http.Method, string, string) {
8 if attrs.len == 0 {
9 return [http.Method.get], '/${name}', ''
10 }
11
12 mut x := attrs.clone()
13 mut methods := []http.Method{}
14 mut path := ''
15 mut host := ''
16
17 for i := 0; i < x.len; {
18 attr := x[i]
19 attru := attr.to_upper()
20 m := http.method_from_str(attru)
21 if attru == 'UNSAFE' {
22 x.delete(i)
23 continue
24 }
25 if attru == 'GET' || m != .get {
26 methods << m
27 x.delete(i)
28 continue
29 }
30 if attr.starts_with('/') {
31 if path != '' {
32 return http.MultiplePathAttributesError{}
33 }
34 path = attr
35 x.delete(i)
36 continue
37 }
38 if attr.starts_with('host:') {
39 host = attr.all_after('host:').trim_space().trim('\'"')
40 x.delete(i)
41 continue
42 }
43 i++
44 }
45 if x.len > 0 {
46 return http.UnexpectedExtraAttributeError{
47 attributes: x
48 }
49 }
50 if methods.len == 0 {
51 methods = [http.Method.get]
52 }
53 if path == '' {
54 path = '/${name}'
55 }
56 // Make host lowercase for case-insensitive comparisons
57 return methods, path, host.to_lower()
58}
59
60fn parse_query_from_url(url urllib.URL) map[string]string {
61 mut query := map[string]string{}
62 for qvalue in url.query().data {
63 query[qvalue.key] = qvalue.value
64 }
65 return query
66}
67
68const boundary_start = 'boundary='
69
70struct FileData {
71pub:
72 filename string
73 content_type string
74 data string
75}
76
77// TODO: fix windows files? (CLRF) issues, maybe it is in the `net` module
78fn parse_form_from_request(request http.Request) !(map[string]string, map[string][]http.FileData) {
79 if request.method !in [http.Method.post, .put, .patch] {
80 return map[string]string{}, map[string][]http.FileData{}
81 }
82 ct := request.header.get(.content_type) or { '' }.split(';').map(it.trim_left(' \t'))
83 if 'multipart/form-data' in ct {
84 boundaries := ct.filter(it.starts_with(boundary_start))
85 if boundaries.len != 1 {
86 return error('detected more that one form-data boundary')
87 }
88 boundary := boundaries[0].all_after(boundary_start)
89 if boundary.len > 0 && boundary[0] == `"` {
90 // quotes are send by our http.post_multipart_form/2:
91 return http.parse_multipart_form(request.data, boundary.trim('"'))
92 }
93 // Firefox and other browsers, do not use quotes around the boundary:
94 return http.parse_multipart_form(request.data, boundary)
95 }
96 return http.parse_form(request.data), map[string][]http.FileData{}
97}
98
99// has_route_attributes checks if a method has attributes that indicate it should be a route handler
100fn has_route_attributes(attrs []string) bool {
101 if attrs.len == 0 {
102 return false
103 }
104 for attr in attrs {
105 attru := attr.to_upper()
106 if attru in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] {
107 return true
108 }
109 if attr.starts_with('/') {
110 return true
111 }
112 if attr.starts_with('host:') {
113 return true
114 }
115 }
116 return false
117}
118