Skip to content

Commit 58ef58a

Browse files
committed
yap router
1 parent d695e17 commit 58ef58a

File tree

6 files changed

+1153
-3
lines changed

6 files changed

+1153
-3
lines changed

context.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,17 @@ type Context struct {
3232
engine *Engine
3333
}
3434

35-
func (p *Context) FormInt(name string, defval int) int {
36-
ret := p.FormValue(name)
35+
func (p *Context) setParam(name, val string) {
36+
p.ParseForm()
37+
p.Form.Set(name, val)
38+
}
39+
40+
func (p *Context) Param(name string) string {
41+
return p.FormValue(name)
42+
}
43+
44+
func (p *Context) ParamInt(name string, defval int) int {
45+
ret := p.Param(name)
3746
if ret != "" {
3847
if v, err := strconv.Atoi(ret); err == nil {
3948
return v

demo/hello/hello.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package main
2+
3+
import (
4+
"github.com/goplus/yap"
5+
)
6+
7+
func main() {
8+
y := yap.New()
9+
y.GET("/p/:id", func(ctx *yap.Context) {
10+
ctx.JSON(200, yap.H{
11+
"id": ctx.Param("id"),
12+
})
13+
})
14+
y.Handle("/", func(ctx *yap.Context) {
15+
ctx.TEXT(200, "text/html", `<html><body>Hello, <a href="/p/123">Yap</a>!</body></html>`)
16+
})
17+
y.Run(":8080")
18+
}

internal/url/path.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package url
18+
19+
// CleanPath is the URL version of path.Clean, it returns a canonical URL path
20+
// for p, eliminating . and .. elements.
21+
//
22+
// The following rules are applied iteratively until no further processing can
23+
// be done:
24+
// 1. Replace multiple slashes with a single slash.
25+
// 2. Eliminate each . path name element (the current directory).
26+
// 3. Eliminate each inner .. path name element (the parent directory)
27+
// along with the non-.. element that precedes it.
28+
// 4. Eliminate .. elements that begin a rooted path:
29+
// that is, replace "/.." by "/" at the beginning of a path.
30+
//
31+
// If the result of this process is an empty string, "/" is returned
32+
func CleanPath(p string) string {
33+
const stackBufSize = 128
34+
35+
// Turn empty string into "/"
36+
if p == "" {
37+
return "/"
38+
}
39+
40+
// Reasonably sized buffer on stack to avoid allocations in the common case.
41+
// If a larger buffer is required, it gets allocated dynamically.
42+
buf := make([]byte, 0, stackBufSize)
43+
44+
n := len(p)
45+
46+
// Invariants:
47+
// reading from path; r is index of next byte to process.
48+
// writing to buf; w is index of next byte to write.
49+
50+
// path must start with '/'
51+
r := 1
52+
w := 1
53+
54+
if p[0] != '/' {
55+
r = 0
56+
57+
if n+1 > stackBufSize {
58+
buf = make([]byte, n+1)
59+
} else {
60+
buf = buf[:n+1]
61+
}
62+
buf[0] = '/'
63+
}
64+
65+
trailing := n > 1 && p[n-1] == '/'
66+
67+
// A bit more clunky without a 'lazybuf' like the path package, but the loop
68+
// gets completely inlined (bufApp calls).
69+
// So in contrast to the path package this loop has no expensive function
70+
// calls (except make, if needed).
71+
72+
for r < n {
73+
switch {
74+
case p[r] == '/':
75+
// empty path element, trailing slash is added after the end
76+
r++
77+
78+
case p[r] == '.' && r+1 == n:
79+
trailing = true
80+
r++
81+
82+
case p[r] == '.' && p[r+1] == '/':
83+
// . element
84+
r += 2
85+
86+
case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'):
87+
// .. element: remove to last /
88+
r += 3
89+
90+
if w > 1 {
91+
// can backtrack
92+
w--
93+
94+
if len(buf) == 0 {
95+
for w > 1 && p[w] != '/' {
96+
w--
97+
}
98+
} else {
99+
for w > 1 && buf[w] != '/' {
100+
w--
101+
}
102+
}
103+
}
104+
105+
default:
106+
// Real path element.
107+
// Add slash if needed
108+
if w > 1 {
109+
bufApp(&buf, p, w, '/')
110+
w++
111+
}
112+
113+
// Copy element
114+
for r < n && p[r] != '/' {
115+
bufApp(&buf, p, w, p[r])
116+
w++
117+
r++
118+
}
119+
}
120+
}
121+
122+
// Re-append trailing slash
123+
if trailing && w > 1 {
124+
bufApp(&buf, p, w, '/')
125+
w++
126+
}
127+
128+
// If the original string was not modified (or only shortened at the end),
129+
// return the respective substring of the original string.
130+
// Otherwise return a new string from the buffer.
131+
if len(buf) == 0 {
132+
return p[:w]
133+
}
134+
return string(buf[:w])
135+
}
136+
137+
// Internal helper to lazily create a buffer if necessary.
138+
// Calls to this function get inlined.
139+
func bufApp(buf *[]byte, s string, w int, c byte) {
140+
b := *buf
141+
if len(b) == 0 {
142+
// No modification of the original string so far.
143+
// If the next character is the same as in the original string, we do
144+
// not yet have to allocate a buffer.
145+
if s[w] == c {
146+
return
147+
}
148+
149+
// Otherwise use either the stack buffer, if it is large enough, or
150+
// allocate a new buffer on the heap, and copy all previous characters.
151+
if l := len(s); l > cap(b) {
152+
*buf = make([]byte, len(s))
153+
} else {
154+
*buf = (*buf)[:l]
155+
}
156+
b = *buf
157+
158+
copy(b, s[:w])
159+
}
160+
b[w] = c
161+
}

0 commit comments

Comments
 (0)