Full Code of flycash/toy-web for AI

main 06bd53c25b60 cached
96 files
88.0 KB
31.6k tokens
342 symbols
1 requests
Download .txt
Repository: flycash/toy-web
Branch: main
Commit: 06bd53c25b60
Files: 96
Total size: 88.0 KB

Directory structure:
gitextract_k1h4qj8l/

├── .gitignore
├── LICENSE
├── README.md
├── demo/
│   ├── filters/
│   │   └── filter.go
│   └── user.go
├── examples/
│   ├── first_lesson/
│   │   ├── afterclass/
│   │   │   ├── fibonacci.go
│   │   │   ├── fmt.go
│   │   │   └── slice.go
│   │   ├── array_slice/
│   │   │   ├── array.go
│   │   │   └── slice.go
│   │   ├── fmt/
│   │   │   └── fmt.go
│   │   ├── for/
│   │   │   └── for.go
│   │   ├── func_dec/
│   │   │   └── funcs.go
│   │   ├── if_else/
│   │   │   └── ifelse.go
│   │   ├── package_dec/
│   │   │   ├── multi_same/
│   │   │   │   ├── a.go
│   │   │   │   └── b.go
│   │   │   └── not_same/
│   │   │       └── not_same.go
│   │   ├── switch/
│   │   │   └── switch.go
│   │   ├── types/
│   │   │   ├── rune.go
│   │   │   └── string.go
│   │   └── var_and_const/
│   │       ├── assignment.go
│   │       ├── const.go
│   │       ├── var.go
│   │       └── var_wrong.go
│   ├── forth_lesson/
│   │   ├── atomic/
│   │   │   └── atomic.go
│   │   ├── channel/
│   │   │   └── channel.go
│   │   ├── context/
│   │   │   └── context.go
│   │   ├── init/
│   │   │   ├── init_order.go
│   │   │   └── multi_init.go
│   │   ├── select/
│   │   │   └── select.go
│   │   └── static_resource/
│   │       └── file_server.go
│   ├── second_lesson/
│   │   ├── afterclass/
│   │   │   ├── set.go
│   │   │   └── tree.go
│   │   ├── composition/
│   │   │   ├── composition.go
│   │   │   └── no_over_write.go
│   │   ├── http/
│   │   │   └── request_body.go
│   │   ├── map/
│   │   │   └── map.go
│   │   ├── server_context/
│   │   │   └── signup.go
│   │   └── struct/
│   │       ├── intf.go
│   │       ├── pointer.go
│   │       ├── receiver.go
│   │       ├── self_ref.go
│   │       ├── struct.go
│   │       ├── type_a_b.go
│   │       └── type_a_et_b.go
│   └── third_lesson/
│       ├── closure/
│       │   └── closure.go
│       ├── defer/
│       │   └── defer.go
│       ├── errors/
│       │   ├── error.go
│       │   └── panic.go
│       ├── goroutine/
│       │   └── goroutine.go
│       └── sync/
│           ├── map.go
│           ├── mutex.go
│           ├── once.go
│           ├── pool.go
│           └── wait_group.go
├── go.mod
├── go.sum
├── main.go
├── onclass/
│   └── main.go
└── pkg/
    ├── context.go
    ├── filter.go
    ├── graceful_shutdown.go
    ├── graceful_shutdown_signal_darwin.go
    ├── graceful_shutdown_signal_linux.go
    ├── graceful_shutdown_signal_windows.go
    ├── handler.go
    ├── hook.go
    ├── hook_test.go
    ├── map_router.go
    ├── server.go
    ├── static_resource.go
    ├── tree_node.go
    ├── tree_router.go
    ├── tree_router_test.go
    ├── v1/
    │   ├── context.go
    │   ├── filter.go
    │   ├── handler.go
    │   ├── map_router.go
    │   ├── server.go
    │   ├── tree_router.go
    │   └── tree_router_test.go
    ├── v2/
    │   ├── context.go
    │   ├── filter.go
    │   ├── handler.go
    │   ├── map_router.go
    │   ├── server.go
    │   ├── tree_router.go
    │   └── tree_router_test.go
    └── v3/
        ├── context.go
        ├── filter.go
        ├── handler.go
        ├── map_router.go
        ├── server.go
        ├── tree_node.go
        ├── tree_router.go
        └── tree_router_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

.idea


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Ming Deng

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# toy-web
用于极客时间go基础课程


================================================
FILE: demo/filters/filter.go
================================================
package filters

import (
	"fmt"
	web "geektime/toy-web/pkg"
)

func init() {
	web.RegisterFilter("my-custom", myFilterBuilder)
}

func myFilterBuilder(next web.Filter) web.Filter {
	return func(c *web.Context) {
		fmt.Println("假装这是我自定义的 filter")
		next(c)
	}
}


================================================
FILE: demo/user.go
================================================
package demo

import (
	"fmt"
	web "geektime/toy-web/pkg"
	"time"
)

func SignUp(c *web.Context) {
	req := &signUpReq{}
	err := c.ReadJson(req)
	if err != nil {
		_ = c.BadRequestJson(&commonResponse{
			BizCode: 4, // 假如说我们这个代表输入参数错误
			// 注意这里是demo,实际中你应该避免暴露 error
			Msg: fmt.Sprintf("invalid request: %v", err),
		})
		return
	}
	_ = c.OkJson(&commonResponse{
		// 假设这个是新用户的 ID
		Data: 123,
	})
}

func SlowService(c *web.Context) {
	time.Sleep(time.Second * 10)
	_ = c.OkJson(&commonResponse{
		Msg: "Hi, this is msg from slow service",
	})
}

type signUpReq struct {
	Email string `json:"email"`
	Password string `json:"password"`
	ConfirmedPassword string `json:"confirmed_password"`
}

type commonResponse struct {
	BizCode int `json:"biz_code"`
	Msg string `json:"msg"`
	Data interface{} `json:"data"`
}

================================================
FILE: examples/first_lesson/afterclass/fibonacci.go
================================================
package main

func main() {

}

func fibonacci(n int) int {
	// TODO
	return 0
}


================================================
FILE: examples/first_lesson/afterclass/fmt.go
================================================
package main

func main() {
	
}

// 输出两位小数
func printNumWith2(float642 float64) string {
	return ""
}

func printBytes(data []byte) string {
	return ""
}


================================================
FILE: examples/first_lesson/afterclass/slice.go
================================================
package main

func main() {
	s := []int{1, 2, 4, 7}
	// 结果应该是 5, 1, 2, 4, 7
	s = Add(s, 0, 5)

	// 结果应该是5, 9, 1, 2, 4, 7
	s = Add(s, 1, 9)

	// 结果应该是5, 9, 1, 2, 4, 7, 13
	s = Add(s, 6, 13)

	// 结果应该是5, 9, 2, 4, 7, 13
	s = Delete(s, 2)

	// 结果应该是9, 2, 4, 7, 13
	s = Delete(s, 0)

	// 结果应该是9, 2, 4, 7
	s = Delete(s, 4)

}

func Add(s []int, index int, value int) []int {
	//TODO
	return s
}

func Delete(s []int, index int) []int {
	// TODO
	return s
}


================================================
FILE: examples/first_lesson/array_slice/array.go
================================================
package main

import "fmt"

func main() {
	// 直接初始化一个三个元素的数组。大括号里面多一个或者少一个都编译不通过
	a1 := [3]int{9, 8, 7}
	fmt.Printf("a1: %v, len: %d, cap: %d", a1, len(a1), cap(a1))

	// 初始化一个三个元素的数组,所有元素都是0
	var a2 [3]int
	fmt.Printf("a2: %v, len: %d, cap: %d", a2, len(a2), cap(a2))

	//a1 = append(a1, 12) 数组不支持 append 操作

	// 按下标索引
	fmt.Printf("a1[1]: %d", a1[1])
	// 超出下标范围,直接崩溃,编译不通过
	//fmt.Printf("a1[99]: %d", a1[99])
}

================================================
FILE: examples/first_lesson/array_slice/slice.go
================================================
package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3, 4} // 直接初始化了 4 个元素的切片
	fmt.Printf("s1: %v, len %d, cap: %d \n", s1, len(s1), cap(s1))

	s2 := make([]int, 3, 4) // 创建了一个包含三个元素,容量为4的切片
	fmt.Printf("s2: %v, len %d, cap: %d \n", s2, len(s2), cap(s2))

	// s2 目前 [0, 0, 0], append(追加)一个元素,变成什么?
	s2 = append(s2, 7) // 后边添加一个元素,没有超出容量限制,不会发生扩容
	fmt.Printf("s2: %v, len %d, cap: %d \n", s2, len(s2), cap(s2))

	s2 = append(s2, 8) // 后边添加了一个元素,触发扩容
	fmt.Printf("s2: %v, len %d, cap: %d \n", s2, len(s2), cap(s2))

	s3 := make([]int, 4) // 只传入一个参数,表示创建一个含有四个元素,容量也为四个元素的
	// 等价于 s3 := make([]int, 4, 4)
	fmt.Printf("s3: %v, len %d, cap: %d \n", s3, len(s3), cap(s3))

	// 按下标索引
	fmt.Printf("s3[2]: %d", s3[2])
	// 超出下标范围,直接崩溃
	// runtime error: index out of range [99] with length 4
	// fmt.Printf("s3[99]: %d", s3[99])

	// SubSlice()

	//shareArr()
}

func SubSlice() {
	s1 := []int{2, 4, 6, 8, 10}
	s2 := s1[1:3]
	fmt.Printf("s2: %v, len %d, cap: %d \n", s2, len(s2), cap(s2))

	s3 := s1[2:]
	fmt.Printf("s3: %v, len %d, cap: %d \n", s3, len(s3), cap(s3))

	s4 := s1[:3]
	fmt.Printf("s4: %v, len %d, cap: %d \n", s4, len(s4), cap(s4))
}

func ShareSlice() {

	s1 := []int{1, 2, 3, 4}
	s2 := s1[2:]
	fmt.Printf("s1: %v, len %d, cap: %d \n", s1, len(s1), cap(s1))
	fmt.Printf("s2: %v, len %d, cap: %d \n", s2, len(s2), cap(s2))

	s2[0] = 99
	fmt.Printf("s1: %v, len %d, cap: %d \n", s1, len(s1), cap(s1))
	fmt.Printf("s2: %v, len %d, cap: %d \n", s2, len(s2), cap(s2))

	s2 = append(s2, 199)
	fmt.Printf("s1: %v, len %d, cap: %d \n", s1, len(s1), cap(s1))
	fmt.Printf("s2: %v, len %d, cap: %d \n", s2, len(s2), cap(s2))

	s2[1] = 1999
	fmt.Printf("s1: %v, len %d, cap: %d \n", s1, len(s1), cap(s1))
	fmt.Printf("s2: %v, len %d, cap: %d \n", s2, len(s2), cap(s2))
}


================================================
FILE: examples/first_lesson/fmt/fmt.go
================================================
package main

import "fmt"

func main() {
	name:="Tom"
	age := 17
	// 这个 API 是返回字符串的,所以大多数时候我们都是用这个
	str := fmt.Sprintf("hello, I am %s, I am %d years old \n", name, age)
	println(str)

	// 这个是直接输出,一般简单程序 DEBUG 会用它输出到一些信息到控制台
	fmt.Printf("hello, I am %s, I am %d years old \n", name, age)

	replaceHolder()
}

func replaceHolder() {
	u := &user{
		Name: "Tom",
		Age: 17,
	}
	fmt.Printf("v => %v \n", u)
	fmt.Printf("+v => %+v \n", u)
	fmt.Printf("#v => %#v \n", u)
	fmt.Printf("T => %T \n", u)
}

type user struct {
	Name string
	Age int
}


================================================
FILE: examples/first_lesson/for/for.go
================================================
package main

import "fmt"

func main() {
	ForLoop()
	ForI()
	ForR()
}

func ForLoop()  {
	arr := []int {9, 8, 7, 6}
	index := 0
	for {
		if index == 3{
			// break 跳出循环
			break
		}
		fmt.Printf("%d => %d\n", index, arr[index])
		index ++
	}
	fmt.Println(" for loop end \n ")
}

func ForI()  {
	arr := []int {9, 8, 7, 6}
	for i := 0; i < len(arr); i++ {
		fmt.Printf("%d => %d \n", i, arr[i])
	}
	fmt.Println("for i loop end \n ")
}

func ForR()  {
	arr := []int {9, 8, 7, 6}

	for index, value := range arr {
		fmt.Printf("%d => %d\n", index, value)
	}

	// 如果只是需要 value, 可以用 _ 代替 index
	for _, value := range arr {
		fmt.Printf("only value: %d \n", value)
	}

	// 如果只需要 index 也可以去掉 写成 for index := range arr
	for index := range arr {
		fmt.Printf("only index: %d \n", index)
	}

	fmt.Println("for r loop end \n ")
}











================================================
FILE: examples/first_lesson/func_dec/funcs.go
================================================
package main

import "fmt"

func main() {
	a := Fun0("Tom")
	println(a)

	b, c := Fun1("a", 17)
	println(b)
	println(c)

	_, d := Fun2("a", "b")
	println(d)

	// 不定参数后面可以传递任意多个值
	Fun4("hello", 19, "CUICUI", "DaMing")
	s := []string{"CUICUI", "DaMing"}
	Fun4("hello", 19, s...)
}

// Fun0 只有一个返回值,不需要括号括起来
func Fun0(name string) string {
	return "Hello, " + name
}

// Fun1 多个参数,多个返回值。参数有名字,但是返回值没有
func Fun1(a string, b int) (int, string) {
	return 0, "你好"
}

// Fun2 的返回值具有名字,可以在内部直接复制,然后返回
// 也可以忽略age, name,直接返回别的。
func Fun2(a string, b string) (age int, name string) {
	age = 19
	name = "Tom"
	return
	//return 19, "Tom" // 这样返回也可以
}

// Fun3 多个参数具有相同类型放在一起,可以只写一次类型
func Fun3(a, b, c string, abc, bcd int, p string) (d, e int, g string) {
	d = 15
	e = 16
	g = "你好"
	return
	//return 0, 0, "你好" // 这样也可以
}

// Fun4 不定参数。不定参数要放在最后面
func Fun4(a string, b int, names...string)  {
	// 我们使用的时候可以直接把 names 看做切片
	for _, name := range names {
		fmt.Printf("不定参数:%s \n", name)
	}
}

================================================
FILE: examples/first_lesson/if_else/ifelse.go
================================================
package main

import "fmt"

func main() {
	Young(9)
	Young(100)

	IfUsingNewVariable(10, 200)
	IfUsingNewVariable(100, 30)
}

func Young(age int) {
	if age < 18{
		fmt.Println("I am a child!")
	} else {
		// else 分支也可以没有
		fmt.Println("I not a child")
	}
}

func IfUsingNewVariable(start int, end int) {
	if distance := end - start; distance > 100 {
		fmt.Printf("距离太远,不来了: %d\n", distance)
	} else {
		// else 分支也可以没有
		fmt.Printf("距离并不远,来一趟: %d\n", distance)
	}

	// 这里不能访问  distance
	//fmt.Printf("距离是: %d\n", distance)
}

================================================
FILE: examples/first_lesson/package_dec/multi_same/a.go
================================================
package multi_same


================================================
FILE: examples/first_lesson/package_dec/multi_same/b.go
================================================
package multi_same


================================================
FILE: examples/first_lesson/package_dec/not_same/not_same.go
================================================
package not_same_aaaa


================================================
FILE: examples/first_lesson/switch/switch.go
================================================
package main

import "fmt"

func main() {
	ChooseFruit("蓝莓")
	ChooseFruit("苹果")
	ChooseFruit("西瓜")
}

func ChooseFruit(fruit string) {
	switch fruit {
	case "苹果":
		fmt.Println("这是一个苹果")
	case "草莓", "蓝莓":
		fmt.Println("这是霉霉")
	default:
		fmt.Printf("不知道是啥:%s \n", fruit)
	}
}


================================================
FILE: examples/first_lesson/types/rune.go
================================================
package main

func main() {
	var a byte = 13
}


================================================
FILE: examples/first_lesson/types/string.go
================================================
package main

import "unicode/utf8"

func main() {
	// 一般推荐用于短的,不用换行的,不含双引号的
	println("He said:\" Hello Go \" ")
	// 长的,复杂的。比如说放个 json 串
	println(`He said: "hello, Go"
我还可以换个行
`)


	println(len("你好")) // 输出6
	println(utf8.RuneCountInString("你好")) // 输出 2
	println(utf8.RuneCountInString("你好ab")) // 输出 4

	// 反正遇到计算字符个数,比如说用户名字多长,博客多长这种字符个数
	// 记得用 utf8.RuneCountInString

	// 字符串拼接。只能发生在 string 之间
	println("Hello, " + "Go!")

}


================================================
FILE: examples/first_lesson/var_and_const/assignment.go
================================================
package main

func main() {
	 a := 13
	 println(a)
	 b := "你好"
	 println(b)
}



================================================
FILE: examples/first_lesson/var_and_const/const.go
================================================
package main

const internal = "包内可访问"
const External = "包外可访问"

func main() {
	const a = "你好"
	println(a)
}


================================================
FILE: examples/first_lesson/var_and_const/var.go
================================================
package main

// Global 首字母大写,全局可以访问
var Global = "全局变量"

// 首字母小写,只能在这个包里面使用
// 其子包也不能用
var local = "包变量"

var (
	First string = "abc"
	second int32 = 16
)

func main() {
	// int 是灰色的,是因为 golang 自己可以做类型推断,它觉得你可以省略
	var a int = 13
	println(a)

	// 这里我们省略了类型
	var b = 14
	println(b)

	// 这里 uint 不可省略,因为生路之后,因为不加 uint 类型,15会被解释为 int 类型
	var c uint = 15
	println(c)

	// 这一句无法通过编译,因为 golang 是强类型语言,并且不会帮你做任何的转换
	// println(a == c)

	// 只声明不赋值,d 是默认值 0,类型不可以省略
	var d int
	println(d)
}





================================================
FILE: examples/first_lesson/var_and_const/var_wrong.go
================================================
package main

var aa = "hello"
// var aa = "bbb" 这个包已经有一个 a 了,所以再次声明会导致编译
func main() {
	aa := 13 // 虽然包外面已经有一个 aa 了,但是这里从包变成了局部变量
	println(aa)

	var bb = 15
	//var bb = 16 // 重复声明,也会导致编译不通过
	println(bb)

	bb = 17 // OK,没有重复声明,只是赋值了新的值
	// bb := 18 // 不行,因为 := 就是声明并且赋值的简写,相当于重复声明了 bb
}


================================================
FILE: examples/forth_lesson/atomic/atomic.go
================================================
package main

import "sync/atomic"

var value int32 = 0
func main() {
	// 要传入 value 的指针
	// 把 value + 10
	atomic.AddInt32(&value, 10)
	nv := atomic.LoadInt32(&value)
	// 输出10
	println(nv)
	// 如果之前的值是10,那么就设置为新的值 20
	swapped := atomic.CompareAndSwapInt32(&value, 10, 20)
	// 输出 true
	println(swapped)

	// 如果之前的值是19,那么就设置为新的值 50
	// 显然现在 value 是 20
	swapped = atomic.CompareAndSwapInt32(&value, 19, 50)
	// 输出 false
	println(swapped)

	old := atomic.SwapInt32(&value, 40)
	// 应该是20,即原本的值
	println(old)
	// 输出新的值,也就是交换后的值,40
	println(value)
}

================================================
FILE: examples/forth_lesson/channel/channel.go
================================================
package main

import (
	"fmt"
	"time"
)

func main() {
	channelWithoutCache()
	channelWithCache()
}

func channelWithCache()  {
	ch := make(chan string, 1)
	go func() {

		ch <- "Hello, first msg from channel"
		time.Sleep(time.Second)
		ch <- "Hello, second msg from channel"
	}()

	time.Sleep(2 * time.Second)
	msg := <- ch
	fmt.Println(time.Now().String() + msg)
	msg = <- ch
	fmt.Println(time.Now().String() + msg)
	// 因为前面我们先睡了2秒,所以其实会有一个已经在缓冲了
	// 当我们尝试输出的时候,这个输出间隔就会明显小于1秒
	// 我电脑上的几次实验,差距都在1ms以内
}

func channelWithoutCache() {
	// 不带缓冲
	ch := make(chan string)
	go func() {
		time.Sleep(time.Second)
		ch <- "Hello, msg from channel"
	}()

	// 这里比较容易写成 msg <- ch,编译会报错
	msg := <- ch
	fmt.Println(msg)
}


================================================
FILE: examples/forth_lesson/context/context.go
================================================
package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	WithTimeout()
	WithCancel()
	WithDeadline()
	WithValue()
}

func WithTimeout() {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second * 2)
	defer cancel()

	start := time.Now().Unix()
	<- ctx.Done()
	end := time.Now().Unix()
	// 输出2,说明在 ctx.Done()这里阻塞了两秒
	fmt.Println(end-start)
}

func WithCancel() {
	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		<- ctx.Done()
		fmt.Println("context was canceled")
	}()
	// 确保我们的 goroutine进去执行了
	time.Sleep(time.Second)
	cancel()
	// 确保后面那句打印出来了
	time.Sleep(time.Second)
}

func WithDeadline() {
	// 设置两秒后超时
	ctx, cancel := context.WithDeadline(context.Background(),
		time.Now().Add(2 * time.Second))
	defer cancel()

	start := time.Now().Unix()
	<- ctx.Done()
	end := time.Now().Unix()
	// 输出2,说明在 ctx.Done()这里阻塞了两秒
	fmt.Println(end-start)
}

func WithValue() {
	parentKey := "parent"
	parent := context.WithValue(context.Background(), parentKey, "this is parent")

	sonKey := "son"
	son := context.WithValue(parent, sonKey, "this is son")

	// 尝试从 parent 里面拿出来 key = son的,会拿不到
	if parent.Value(parentKey) == nil {
		fmt.Printf("parent can not get son's key-value pair")
	}

	if val := son.Value(parentKey); val != nil {
		fmt.Printf("parent can not get son's key-value pair")
	}
}


================================================
FILE: examples/forth_lesson/init/init_order.go
================================================
package main

func init() {
	// 因为我们不能确定 init 方法的执行顺序,
	// 只能曲线救国
	initBeforeSomething()
	initSomething()
	initAfterSomething()
}

func initBeforeSomething()  {
	
}

func initSomething()  {
	
}

func initAfterSomething()  {
	
}

================================================
FILE: examples/forth_lesson/init/multi_init.go
================================================
package main

func init() {
	// 第一个
}

func init() {
	// 第二个
}


================================================
FILE: examples/forth_lesson/select/select.go
================================================
package main

import (
	"fmt"
	"time"
)

func main() {
	// 这个不能在 main 函数运行,是因为运行起来,
	// 所有的goroutine都被我们搞sleep了,直接就崩了
	//Select()
}

func Select() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go func() {
		time.Sleep(time.Second)
		ch1 <- "msg from channel1"
	}()

	go func() {
		time.Sleep(time.Second)
		ch2 <- "msg from channel2"
	}()

	for {
		select {
		case msg := <- ch1:
			fmt.Println(msg)
		case msg := <- ch2:
			fmt.Println(msg)
		}
	}
}


================================================
FILE: examples/forth_lesson/static_resource/file_server.go
================================================
package main

import "net/http"

func main() {
	serve := http.FileServer(http.Dir("."))
	//http.Handle("/", serve)
	http.ListenAndServe(":8080", serve)
}


================================================
FILE: examples/second_lesson/afterclass/set.go
================================================
package afterclass

type Set interface {
	Put(key string)
	Keys() []string
	Contains(key string) bool
	Remove(key string)
	// 如果之前已经有了,就返回旧的值,absent =false
	// 如果之前没有,就塞下去,返回 absent = true
	PutIfAbsent(key string) (old string, absent bool)
}


================================================
FILE: examples/second_lesson/afterclass/tree.go
================================================
package afterclass

type Tree interface {

}

// 二叉树
type binaryTree struct {

}

// 多叉树
type mutliWayTree struct {

}


================================================
FILE: examples/second_lesson/composition/composition.go
================================================
package main

import "fmt"

func main() {

}

// Swimming 会游泳的
type Swimming interface {
	Swim()
}

type Duck interface {
	// 鸭子是会游泳的,所以这里组合了它
	Swimming
}


type Base struct {
	Name string
}

type Concrete1 struct {
	Base
}

type Concrete2 struct {
	*Base
}

func (c Concrete1) SayHello() {
	// c.Name 直接访问了Base的Name字段
	fmt.Printf("I am base and my name is: %s \n", c.Name)
	// 这样也是可以的
	fmt.Printf("I am base and my name is: %s \n", c.Base.Name)

	// 调用了被组合的
	c.Base.SayHello()
}

func (b *Base) SayHello() {
	fmt.Printf("I am base and my name is: %s \n", b.Name)
}

================================================
FILE: examples/second_lesson/composition/no_over_write.go
================================================
package main

import "fmt"

func main() {
	son := Son{
		Parent{},
	}

	son.SayHello()
}

type Parent struct {

}

func (p Parent) SayHello() {
	fmt.Println("I am " + p.Name())
}

func (p Parent) Name() string {
	return "Parent"
}

type Son struct {
	Parent
}

// 定义了自己的 Name() 方法
func (s Son) Name() string {
	return "Son"
}



================================================
FILE: examples/second_lesson/http/request_body.go
================================================
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

func home(w http.ResponseWriter, r *http.Request)  {
	fmt.Fprint(w, "Hi, this is home page")
}

func readBodyOnce(w http.ResponseWriter, r *http.Request)  {
	body, err := io.ReadAll(r.Body)
	if err != nil {
		fmt.Fprintf(w, "read body failed: %v", err)
		// 记住要返回,不然就还会执行后面的代码
		return
	}
	// 类型转换,将 []byte 转换为 string
	fmt.Fprintf(w, "read the data: %s \n", string(body))

	// 尝试再次读取,啥也读不到,但是也不会报错
	body, err = io.ReadAll(r.Body)
	if err != nil {
		// 不会进来这里
		fmt.Fprintf(w, "read the data one more time got error: %v", err)
		return
	}
	fmt.Fprintf(w, "read the data one more time: [%s] and read data length %d \n", string(body), len(body))
}


func getBodyIsNil(w http.ResponseWriter, r *http.Request) {
	if r.GetBody == nil {
		fmt.Fprint(w, "GetBody is nil \n")
	} else {
		fmt.Fprintf(w, "GetBody not nil \n")
	}
}

func queryParams(w http.ResponseWriter, r *http.Request) {
	values := r.URL.Query()
	fmt.Fprintf(w, "query is %v\n", values)
}

func wholeUrl(w http.ResponseWriter, r *http.Request)  {
	data, _ := json.Marshal(r.URL)
	fmt.Fprintf(w, string(data))
}

func header(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "header is %v\n", r.Header)
}

func form(w http.ResponseWriter, r *http.Request)  {
	fmt.Fprintf(w, "before parse form %v\n", r.Form)
	err := r.ParseForm()
	if err != nil {
		fmt.Fprintf(w, "parse form error %v\n", r.Form)
	}
	fmt.Fprintf(w, "before parse form %v\n", r.Form)
}

func main() {
	http.HandleFunc("/", home)
	http.HandleFunc("/body/once", readBodyOnce)
	http.HandleFunc("/body/multi", getBodyIsNil)
	http.HandleFunc("/url/query", queryParams)
	http.HandleFunc("/header", header)
	http.HandleFunc("/wholeUrl", wholeUrl)
	http.HandleFunc("/form", form)
	if err := http.ListenAndServe(":8080", nil); err != nil {
		panic(err)
	}
}

================================================
FILE: examples/second_lesson/map/map.go
================================================
package main

import "fmt"

func main() {
	// 创建了一个预估容量是2的 map
	m := make(map[string]string, 2)
	// 没有指定预估容量
	m1 := make(map[string]string)
	// 直接初始化
	m2 := map[string]string{
		"Tom": "Jerry",
	}

	// 赋值
	m["hello"] = "world"
	m1["hello"] = "world"
	// 赋值
	m2["hello"] = "world"
	// 取值
	val := m["hello"]
	println(val)

	// 再次取值,使用两个返回值,后面的ok会告诉你map有没有这个key
	val, ok := m["invalid_key"]
	if !ok {
		println("key not found")
	}

	for key, val := range m {
		fmt.Printf("%s => %s \n", key, val)
	}
}

================================================
FILE: examples/second_lesson/server_context/signup.go
================================================
package server_context

import (
	"encoding/json"
	"fmt"
	"geektime/toy-web/pkg/v2"
	"io"
	"net/http"
)

// 在没有 context 抽象的情况下,是长这样的
func SignUpWithoutContext(w http.ResponseWriter, r *http.Request) {
	req := &signUpReq{}
	body, err := io.ReadAll(r.Body)
	if err != nil {
		fmt.Fprintf(w, "read body failed: %v", err)
		// 要返回掉,不然就会继续执行后面的代码
		return
	}
	err = json.Unmarshal(body, req)
	if err != nil {
		fmt.Fprintf(w, "deserialized failed: %v", err)
		// 要返回掉,不然就会继续执行后面的代码
		return
	}

	// 返回一个虚拟的 user id 表示注册成功了
	fmt.Fprintf(w, "%d", err)
}

func SignUpWithoutWrite(w http.ResponseWriter, r *http.Request) {
	c := webv2.NewContext(w, r)
	req := &signUpReq{}
	err := c.ReadJson(req)
	if err != nil {
		resp := &commonResponse{
			BizCode: 4, // 假如说我们这个代表输入参数错误
			Msg: fmt.Sprintf("invalid request: %v", err),
		}
		respBytes, _ := json.Marshal(resp)
		fmt.Fprint(w, string(respBytes))
		return
	}
	// 这里又得来一遍 resp 转json
	fmt.Fprintf(w, "invalid request: %v", err)
}

type signUpReq struct {
	Email string `json:"email"`
	Password string `json:"password"`
	ConfirmedPassword string `json:"confirmed_password"`
}

type commonResponse struct {
	BizCode int `json:"biz_code"`
	Msg string `json:"msg"`
	Data interface{} `json:"data"`
}


================================================
FILE: examples/second_lesson/struct/intf.go
================================================
package main

// 首字母小写,所以是一个包私有的接口
type animal interface {
	// 这里可以有任意多个方法,不过我们一般建议是小接口,
	// 即接口里面不会有很多方法
	// 方法声明不需要 func 关键字

	Eat()
}

// 首字母大写,所以是一个包外可访问的接口
type Duck interface {
	Swim()
}


================================================
FILE: examples/second_lesson/struct/pointer.go
================================================
package main

import "fmt"

func main() {
	// 指针用 * 表示
	var p *ToyDuck = &ToyDuck{}
	// 解引用,得到结构体
	var duck ToyDuck = *p
	duck.Swim()

	// 只是声明了,但是没有使用
	var nilDuck *ToyDuck
	if nilDuck == nil {
		fmt.Println("nilDuck is nil")
	}
}

================================================
FILE: examples/second_lesson/struct/receiver.go
================================================
package main

import "fmt"

func main() {

	// 因为 u 是结构体,所以方法调用的时候它数据是不会变的
	u := User{
		Name: "Tom",
		Age: 10,
	}
	u.ChangeName("Tom Changed!")
	u.ChangeAge(100)
	fmt.Printf("%v \n", u)

	// 因为 up 指针,所以内部的数据是可以被改变的
	up := &User{
		Name: "Jerry",
		Age: 12,
	}

	// 因为 ChangeName 的接收器是结构体
	// 所以 up 的数据还是不会变
	up.ChangeName("Jerry Changed!")
	up.ChangeAge(120)

	fmt.Printf("%v \n", up)
}

type User struct {
	Name string
	Age int
}

// 结构体接收器
func (u User) ChangeName(newName string)  {
	u.Name = newName
}

// 指针接收器
func (u *User) ChangeAge(newAge int) {
	u.Age = newAge
}


================================================
FILE: examples/second_lesson/struct/self_ref.go
================================================
package main

func main() {

}

type Node struct {
	//自引用只能使用指针
	//left Node
	//right Node

	left *Node
	right *Node

	// 这个也会报错
	// nn NodeNode
}


type NodeNode struct {
	node Node
}

================================================
FILE: examples/second_lesson/struct/struct.go
================================================
package main

import "fmt"

func main() {
	// duck1 是 *ToyDuck
	duck1 := &ToyDuck{}
	duck1.Swim()

	duck2 := ToyDuck{}
	duck2.Swim()

	// duck3 是 *ToyDuck
	duck3 := new(ToyDuck)
	duck3.Swim()

	// 当你声明这样的时候,Go 就帮你分配好内存
	// 不用担心空指针的问题,以为它压根就不是指针
	var duck4 ToyDuck
	duck4.Swim()

	// duck5 就是一个指针了
	var duck5 *ToyDuck
	// 这边会直接panic 掉
	duck5.Swim()

	// 赋值,初始化按字段名字赋值
	duck6 := ToyDuck{
		Color: "黄色",
		Price: 100,
	}
	duck6.Swim()

	// 初始化按字段顺序赋值,不建议使用
	duck7 := ToyDuck{"蓝色", 1024}
	duck7.Swim()

	// 后面再单独赋值
	duck8 := ToyDuck{}
	duck8.Color = "橘色"

}

// ToyDuck 玩具鸭
type ToyDuck struct {
	Color string
	Price uint64
}

func (t *ToyDuck) Swim() {
	fmt.Printf("门前一条河,游过一群鸭,我是%s,%d一只\n", t.Color, t.Price)
}




================================================
FILE: examples/second_lesson/struct/type_a_b.go
================================================
package main

import "fmt"

func main() {
	fake := FakeFish{}
	// fake 无法调用原来 Fish 的方法
	// 这一句会编译错误
	//fake.Swim()
	fake.FakeSwim()

	// 转换为Fish
	td := Fish(fake)
	// 真的变成了鱼
	td.Swim()

	sFake := StrongFakeFish{}
	// 这里就是调用了自己的方法
	sFake.Swim()

	td = Fish(sFake)
	// 真的变成了鱼
	td.Swim()
}

// 定义了一个新类型,注意是新类型
type FakeFish Fish

func (f FakeFish) FakeSwim() {
	fmt.Printf("我是山寨鱼,嘎嘎嘎\n")
}

// 定义了一个新类型
type StrongFakeFish Fish

func (f StrongFakeFish) Swim() {
	fmt.Printf("我是华强北山寨鱼,嘎嘎嘎\n")
}

type Fish struct {
}

func (f Fish) Swim() {
	fmt.Printf("我是鱼,假装自己是一直鸭子\n")
}


================================================
FILE: examples/second_lesson/struct/type_a_et_b.go
================================================
package main

import "fmt"

func main() {
	var n News = fakeNews{
		Name: "hello",
	}
	n.Report()
}

type News struct {
	Name string
}

func (d News) Report() {
	fmt.Println("I am news: " + d.Name)
}

type fakeNews = News

================================================
FILE: examples/third_lesson/closure/closure.go
================================================
package main

import (
	"fmt"
	"time"
)

func main() {

	i := 13
	a := func() {
		fmt.Printf("i is %d \n", i)
	}
	a()

	fmt.Println(ReturnClosure("Tom")())

	Delay()
	time.Sleep(time.Second)
}

func ReturnClosure(name string) func() string {
	return func() string {
		return "Hello, " + name
	}
}

func Delay() {
	fns := make([]func(), 0, 10)
	for i := 0; i < 10; i++ {
		fns = append(fns, func() {
			fmt.Printf("hello, this is : %d \n", i)
		})
	}

	for _, fn := range fns {
		fn()
	}
}


================================================
FILE: examples/third_lesson/defer/defer.go
================================================
package main

import "fmt"

func main() {
	defer func() {
		fmt.Println("aaa")
	}()

	defer func() {
		fmt.Println("bbb")
	}()

	defer func() {
		fmt.Println("ccc")
	}()
}


================================================
FILE: examples/third_lesson/errors/error.go
================================================
package main

import (
	"errors"
	"fmt"
)

func main() {
	var err error = &MyError{}
	println(err.Error())

	ErrorsPkg()
}

type MyError struct {
}

func (m *MyError) Error() string {
	return "Hello, it's my error"
}

func ErrorsPkg()  {
	err := &MyError{}
	// 使用 %w 占位符,返回的是一个新错误
	// wrappedErr 是一个新类型,fmt.wrapError
	wrappedErr := fmt.Errorf("this is an wrapped error %w", err)

	// 再解出来
	if err == errors.Unwrap(wrappedErr) {
		fmt.Println("unwrapped")
	}
	
	if errors.Is(wrappedErr, err) {
		// 虽然被包了一下,但是 Is 会逐层解除包装,判断是不是该错误
		fmt.Println("wrapped is err")
	}

	copyErr := &MyError{}
	// 这里尝试将 wrappedErr转换为 MyError
	// 注意我们使用了两次的取地址符号
	if errors.As(wrappedErr, &copyErr) {
		fmt.Println("convert error")
	}
}



================================================
FILE: examples/third_lesson/errors/panic.go
================================================
package main

import "fmt"

func main() {
	defer func() {
		if data := recover(); data != nil {
			fmt.Printf("hello, panic: %v\n", data)
		}
		fmt.Println("恢复之后从这里继续执行")
	}()

	panic("Boom")
	fmt.Println("这里将不会执行下来")
}


================================================
FILE: examples/third_lesson/goroutine/goroutine.go
================================================
package main

import (
	"fmt"
	"time"
)

func main() {
	GoRoutine()
}

func GoRoutine() {
	go func() {
		time.Sleep(10 * time.Second)
	}()
	// 这里直接输出,不会等待十秒
	fmt.Println("I am here")
}

================================================
FILE: examples/third_lesson/sync/map.go
================================================
package main

import (
	"fmt"
	"sync"
)

func main() {
	m := sync.Map{}
	m.Store("cat", "Tom")
	m.Store("mouse", "Jerry")

	// 这里重新读取出来的,就是
	val, ok := m.Load("cat")
	if ok {
		fmt.Println(len(val.(string)))
	}
}

================================================
FILE: examples/third_lesson/sync/mutex.go
================================================
package main

import (
	"sync"
)

var mutex sync.Mutex
var rwMutex sync.RWMutex
func Mutex() {
	mutex.Lock()
	defer mutex.Unlock()
	// 你的代码
}

func RwMutex()  {
	// 加读锁
	rwMutex.RLock()
	defer rwMutex.RUnlock()

	// 也可以加写锁
	rwMutex.Lock()
	defer rwMutex.Unlock()
}

// 不可重入例子
func Failed1()  {
	mutex.Lock()
	defer mutex.Unlock()

	// 这一句会死锁
	// 但是如果你只有一个goroutine,那么这一个会导致程序崩溃
	mutex.Lock()
	defer mutex.Unlock()
}

// 不可升级
func Failed2()  {
	rwMutex.RLock()
	defer rwMutex.RUnlock()

	// 这一句会死锁
	// 但是如果你只有一个goroutine,那么这一个会导致程序崩溃
	mutex.Lock()
	defer mutex.Unlock()
}


================================================
FILE: examples/third_lesson/sync/once.go
================================================
package main

import (
	"fmt"
	"sync"
)

func main() {
	PrintOnce()
	PrintOnce()
	PrintOnce()
}

var once sync.Once

// 这个方法,不管调用几次,只会输出一次
func PrintOnce() {
	once.Do(func() {
		fmt.Println("只输出一次")
	})
}


================================================
FILE: examples/third_lesson/sync/pool.go
================================================
package main

import "sync"

func main() {
	pool := sync.Pool{
		New: func() interface{}{
			return &user{}
	}}

	// Get 返回的是 interface{},所以需要类型断言
	u := pool.Get().(*user)
	// defer 还回去
	defer pool.Put(u)

	// 紧接着重置 u 这个对象
	u.Reset("Tom", "my_email@qq.com")

	// 下边就是使用 u 来完成你的业务逻辑
}

type user struct {
	Name string
	Email string
}

// 一般来说,复用对象都要求我们取出来之后,
// 重置里面的字段
func (u *user) Reset(name string, email string)  {
	u.Email = email
	u.Name = name
}

================================================
FILE: examples/third_lesson/sync/wait_group.go
================================================
package main

import (
	"fmt"
	"sync"
)

func main() {
	res := 0
	wg := sync.WaitGroup{}
	wg.Add(10)
	for i := 0; i < 10; i++ {
		go func(val int) {
			res += val
			wg.Done()
		}(i)
	}
	// 把这个注释掉你会发现,什么结果你都可能拿到
	wg.Wait()
	fmt.Println(res)
}

================================================
FILE: go.mod
================================================
module geektime/toy-web

go 1.16

require (
	github.com/hashicorp/golang-lru v0.5.4 // indirect
	github.com/stretchr/testify v1.7.0 // indirect
)


================================================
FILE: go.sum
================================================
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: main.go
================================================
package main

import (
	"context"
	"fmt"
	"geektime/toy-web/demo"
	_ "geektime/toy-web/demo/filters"
	"geektime/toy-web/pkg"
	"net/http"
	"time"
)

func home(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "这是主页")
}

func user(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "这是用户")
}

func createUser(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "这是创建用户")
}

func order(w http.ResponseWriter, r *http.Request)  {
	fmt.Fprintf(w, "这是订单")
}

func main() {
	shutdown := web.NewGracefulShutdown()
	server := web.NewSdkHttpServer("my-test-server",
		web.MetricFilterBuilder, shutdown.ShutdownFilterBuilder)
	adminServer := web.NewSdkHttpServer("admin-test-server",
		// 注意,如果你真实环境里面,使用的是多个 server监听不同端口,
		// 那么这个 shutdown最好也是多个。互相之间就不会有竞争
		// MetricFilterBuilder 是无状态的,所以不存在这种问题
		web.MetricFilterBuilder, shutdown.ShutdownFilterBuilder)

	// 注册路由
	_ = server.Route("POST", "/user/create/*", demo.SignUp)
	_ = server.Route("POST", "/slowService", demo.SlowService)

	// 准备静态路由

	staticHandler := web.NewStaticResourceHandler(
		"demo/static", "/static",
		web.WithMoreExtension(map[string]string{
			"mp3": "audio/mp3",
		}), web.WithFileCache(1 << 20, 100))
	// 访问 Get http://localhost:8080/static/forest.png
	server.Route("GET", "/static/*", staticHandler.ServeStaticResource)

	go func() {
		if err := adminServer.Start(":8081"); err != nil {
			panic(err)
		}
	}()

	go func() {
		if err := server.Start(":8080"); err != nil {
			// 快速失败,因为服务器都没启动成功,啥也做不了
			panic(err)
		}
		// 假设我们后面还有很多动作
	}()

	// 先执行 RejectNewRequestAndWaiting,等待所有的请求
	// 然后我们关闭 server,如果是多个 server,可以多个 goroutine 一起关闭
	//
	web.WaitForShutdown(
		func(ctx context.Context) error {
			// 假设我们这里有一个 hook
			// 可以通知网关我们要下线了
			fmt.Println("mock notify gateway")
			time.Sleep(time.Second * 2)
			return nil
		},
		shutdown.RejectNewRequestAndWaiting,
		// 全部请求处理完了我们就可以关闭 server了
		web.BuildCloseServerHook(server, adminServer),
		func(ctx context.Context) error {
			// 假设这里我要清理一些执行过程中生成的临时资源
			fmt.Println("mock release resources")
			time.Sleep(time.Second * 2)
			return nil
		})

	// filterNames := ReadFromConfig
	// 匿名引入之后,就可以在这里按名索引 filter
	//web.NewSdkHttpServerWithFilterNames("my-server", filterNames...)

}






================================================
FILE: onclass/main.go
================================================
package main

import (
	"fmt"
	"net/http"
)

func home(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "这是主页")
}

func user(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "这是用户")
}

func createUser(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "这是创建用户")
}

func order(w http.ResponseWriter, r *http.Request)  {
	fmt.Fprintf(w, "这是订单")
}


func main() {
	http.HandleFunc("/", home)
	http.HandleFunc("/user", user)
	http.HandleFunc("/user/create", createUser)
	http.HandleFunc("/order", order)
	http.ListenAndServe(":8080", nil)
}

type Server interface {
	Route(pattern string, handlerFunc http.HandlerFunc)
	Start(address string) error
}

type sdkHttpServer struct {
	Name string
}


================================================
FILE: pkg/context.go
================================================
package web

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

type Context struct {
	W http.ResponseWriter
	R *http.Request
	PathParams map[string]string
}

func (c *Context) ReadJson(data interface{}) error {
	body, err := io.ReadAll(c.R.Body)
	if err != nil {
		return err
	}
	return json.Unmarshal(body, data)
}
func (c *Context) OkJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusOK, data)
}

func (c *Context) SystemErrJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusInternalServerError, data)
}

func (c *Context) BadRequestJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusBadRequest, data)
}

func (c *Context) WriteJson(status int, data interface{}) error {
	c.W.WriteHeader(status)
	bs, err := json.Marshal(data)
	if err != nil {
		return err
	}
	_, err = c.W.Write(bs)
	if err != nil {
		return err
	}
	return nil
}

func NewContext(w http.ResponseWriter, r *http.Request) *Context {
	return &Context{
		W: w,
		R: r,
		// 一般路径参数都是一个,所以容量1就可以了
		PathParams: make(map[string]string, 1),
	}
}

func newContext() *Context {
	fmt.Println("create new context")
	return &Context{
	}
}

func (c *Context) Reset(w http.ResponseWriter, r *http.Request) {
	c.W = w
	c.R = r
	c.PathParams = make(map[string]string, 1)
}

================================================
FILE: pkg/filter.go
================================================
package web

import (
	"fmt"
	"time"
)

type FilterBuilder func(next Filter) Filter

type Filter func(c *Context)

func MetricFilterBuilder(next Filter) Filter {
	return func(c *Context) {
		// 执行前的时间
		startTime := time.Now().UnixNano()
		next(c)
		// 执行后的时间
		endTime := time.Now().UnixNano()
		fmt.Printf("run time: %d \n", endTime-startTime)
	}
}

var builderMap = make(map[string]FilterBuilder, 4)
func RegisterFilter(name string, builder FilterBuilder)  {
	// 情况1 有些时候你可能不允许重复注册,那么你要先检测是否已经注册过了
	// 情况2 你会在并发的环境下调用这个方法,那么你应该
	builderMap[name] = builder
}

func GetFilterBuilder(name string) FilterBuilder {
	// 如果你觉得名字必须是正确的,那么你同样需要检测
	return builderMap[name]
}

================================================
FILE: pkg/graceful_shutdown.go
================================================
package web

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"sync/atomic"
	"time"
)

var ErrorHookTimeout = errors.New("the hook timeout")

type GracefulShutdown struct {
	// 还在处理中的请求数
	reqCnt int64
	// 大于 1 就说明要关闭了
	closing int32
	
	// 用 channel 来通知已经处理完了所有请求
	zeroReqCnt chan struct{}
}

func NewGracefulShutdown() *GracefulShutdown {
	return &GracefulShutdown{
		zeroReqCnt: make(chan struct{}),
	}
}

// ShutdownFilterBuilder 这个东西怎么保持线程安全呢?
// 它的逻辑有点绕,核心就在于当我们准备关闭的时候,这个动作是单向的,就是说,我的closing一旦加1
// 就再也不会-1
// 所以我们不需要用一个锁把整个方法锁住
// 而实际上,基于这个理由,我们也不需要把 closing 声明为 int32
// 只需要声明 bool,然后在关闭的时候设置为 true。在这里直接检测 true or false就可以。
// 这种做法有一个很重要的点是,在设置值的时候,即便 bool 被高速缓存缓存了,
// 即便了 bool 在平台上,处理器并不能一条指令 设置好值,
// 但是也没什么关系。因为我们可以确认,最终 bool 会变为 true
// 这个做法更加难以理解,所以采用了使用 closing int32 的做法
func (g *GracefulShutdown) ShutdownFilterBuilder(next Filter) Filter {
	return func(c *Context) {
		// 开始拒绝所有的请求
		cl :=  atomic.LoadInt32(&g.closing)
		if cl > 0 {
			c.W.WriteHeader(http.StatusServiceUnavailable)
			return
		}
		atomic.AddInt64(&g.reqCnt, 1)
		next(c)
		n := atomic.AddInt64(&g.reqCnt, -1)
		// 已经开始关闭了,而且请求数为0,
		if cl > 0 && n == 0 {
			g.zeroReqCnt <- struct{}{}
		}
	}
}

// RejectNewRequestAndWaiting 将会拒绝新的请求,并且等待处理中的请求
func (g *GracefulShutdown) RejectNewRequestAndWaiting(ctx context.Context) error {

	atomic.AddInt32(&g.closing, 1)

	// 特殊 case 关闭之前其实就已经处理完了请求。
	if atomic.LoadInt64(&g.reqCnt) == 0 {
		return nil
	}
	
	done := ctx.Done()
	// 因为是单向的,所以我们这里不用 for 在外面包
	// 所谓单向就是,我一触发就回不到原来正常处理请求的状态了
	// 这个 select 可以理解为,要么超时了
	// 要么我这里所有的请求都执行完了
	select {
	case <- done:
		fmt.Println("超时了,还没等到所有请求执行完毕")
		return ErrorHookTimeout
	case <- g.zeroReqCnt:
		fmt.Println("全部请求处理完了")
	}
	return nil
}

func WaitForShutdown(hooks...Hook) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, ShutdownSignals...)
	select {
	case sig := <-signals:
		fmt.Printf("get signal %s, application will shutdown \n", sig)
		// 十分钟都还不行,就直接强退了
		time.AfterFunc(time.Minute * 10, func() {
			fmt.Printf("Shutdown gracefully timeout, application will shutdown immediately. ")
			os.Exit(1)
		})
		for _, h := range hooks {
			ctx, cancel := context.WithTimeout(context.Background(), time.Second * 30)
			err := h(ctx)
			if err != nil {
				fmt.Printf("failed to run hook, err: %v \n", err)
			}
			cancel()
		}
		os.Exit(0)
	}
}


================================================
FILE: pkg/graceful_shutdown_signal_darwin.go
================================================
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package web

import (
	"os"
	"syscall"
)

var (
	// ShutdownSignals receives shutdown signals to process
	ShutdownSignals = []os.Signal{
		os.Interrupt, os.Kill, syscall.SIGKILL, syscall.SIGSTOP,
		syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP,
		syscall.SIGABRT, syscall.SIGSYS, syscall.SIGTERM,
	}
)


================================================
FILE: pkg/graceful_shutdown_signal_linux.go
================================================
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package web

import (
	"os"
	"syscall"
)

var (
	// ShutdownSignals receives shutdown signals to process
	ShutdownSignals = []os.Signal{
		os.Interrupt, os.Kill, syscall.SIGKILL, syscall.SIGSTOP,
		syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP,
		syscall.SIGABRT, syscall.SIGSYS, syscall.SIGTERM,
	}

	// DumpHeapShutdownSignals receives shutdown signals to process
	DumpHeapShutdownSignals = []os.Signal{
		syscall.SIGQUIT, syscall.SIGILL,
		syscall.SIGTRAP, syscall.SIGABRT, syscall.SIGSYS,
	}
)


================================================
FILE: pkg/graceful_shutdown_signal_windows.go
================================================
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package web

import (
	"os"
	"syscall"
)

var (
	// ShutdownSignals receives shutdown signals to process
	ShutdownSignals = []os.Signal{
		os.Interrupt, os.Kill, syscall.SIGKILL,
		syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP,
		syscall.SIGABRT, syscall.SIGTERM,
	}
)

================================================
FILE: pkg/handler.go
================================================
package web

type Handler interface {
	ServeHTTP(c *Context)
	Routable
}

type handlerFunc func(c *Context)

================================================
FILE: pkg/hook.go
================================================
package web

import (
	"context"
	"fmt"
	"sync"
	"time"
)

// Hook 是一个钩子函数。注意,
// ctx 是一个有超时机制的 context.Context
// 所以你必须处理超时的问题
type Hook func(ctx context.Context) error

// BuildCloseServerHook 这里其实可以考虑使用 errgroup,
// 但是我们这里不用是希望每个 server 单独关闭
// 互相之间不影响
func BuildCloseServerHook(servers ...Server) Hook {
	return func(ctx context.Context) error {
		wg := sync.WaitGroup{}
		doneCh := make(chan struct{})
		wg.Add(len(servers))

		for _, s := range servers {
			go func(svr Server) {
				err := svr.Shutdown(ctx)
				if err != nil {
					fmt.Printf("server shutdown error: %v \n", err)
				}
				time.Sleep(time.Second)
				wg.Done()
			}(s)
		}
		go func() {
			wg.Wait()
			doneCh <- struct{}{}
		}()
		select {
		case <- ctx.Done():
			fmt.Printf("closing servers timeout \n")
			return ErrorHookTimeout
		case <- doneCh:
			fmt.Printf("close all servers \n")
			return nil
		}
	}
}


================================================
FILE: pkg/hook_test.go
================================================
package web

import (
	"context"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

func TestBuildCloseServerHook(t *testing.T) {
	svr := NewSdkHttpServer("test-sever")
	h := BuildCloseServerHook(svr, svr, svr, svr, svr)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second * 10)
	defer cancel()
	err := h(ctx)
	assert.Nil(t, err)

	ctx, cancel = context.WithTimeout(context.Background(), time.Millisecond * 10)
	defer cancel()
	err = h(ctx)
	assert.Equal(t, ErrorHookTimeout, err)
}


================================================
FILE: pkg/map_router.go
================================================
package web

import (
	"fmt"
	"net/http"
	"sync"
)

// 一种常用的GO设计模式,
// 用于确保HandlerBasedOnMap肯定实现了这个接口
var _ Handler = &HandlerBasedOnMap{}


type HandlerBasedOnMap struct {
	handlers sync.Map
}

func (h *HandlerBasedOnMap) ServeHTTP(c *Context) {
	request := c.R
	key := h.key(request.Method, request.URL.Path)
	handler, ok := h.handlers.Load(key)
	if !ok {
		c.W.WriteHeader(http.StatusNotFound)
		_, _ = c.W.Write([]byte("not any router match"))
		return
	}

	handler.(handlerFunc)(c)
}

func (h *HandlerBasedOnMap) Route(method string, pattern string,
	handlerFunc handlerFunc) error {
	key := h.key(method, pattern)
	h.handlers.Store(key, handlerFunc)
	return nil
}

func (h *HandlerBasedOnMap) key(method string,
	path string) string {
	return fmt.Sprintf("%s#%s", method, path)
}

func NewHandlerBasedOnMap() *HandlerBasedOnMap {
	return &HandlerBasedOnMap{}
}


================================================
FILE: pkg/server.go
================================================
package web

import (
	"context"
	"fmt"
	"net/http"
	"sync"
	"time"
)

// Routable 可路由的
type Routable interface {
	// Route 设定一个路由,命中该路由的会执行handlerFunc的代码
	Route(method string, pattern string, handlerFunc handlerFunc) error
}

// Server 是http server 的顶级抽象
type Server interface {
	Routable
	// Start 启动我们的服务器
	Start(address string) error

	Shutdown(ctx context.Context) error
}

// sdkHttpServer 这个是基于 net/http 这个包实现的 http server
type sdkHttpServer struct {
	// Name server 的名字,给个标记,日志输出的时候用得上
	Name    string
	handler Handler
	root    Filter
	ctxPool sync.Pool
}

func (s *sdkHttpServer) Route(method string, pattern string,
	handlerFunc handlerFunc) error {
	return s.handler.Route(method, pattern, handlerFunc)
}

func (s *sdkHttpServer) Start(address string) error {
	return http.ListenAndServe(address, s)
}

func (s *sdkHttpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request)  {
	c := s.ctxPool.Get().(*Context)
	defer func() {
		s.ctxPool.Put(c)
	}()
	c.Reset(writer, request)
	s.root(c)
}

func (s *sdkHttpServer) Shutdown(ctx context.Context) error {
	// 因为我们这个简单的框架,没有什么要清理的,
	// 所以我们 sleep 一下来模拟这个过程
	fmt.Printf("%s shutdown...\n", s.Name)
	time.Sleep(time.Second)
	fmt.Printf("%s shutdown!!!\n", s.Name)
	return nil
}

func NewSdkHttpServer(name string, builders ...FilterBuilder) Server {

	// 改用我们的树
	handler := NewHandlerBasedOnTree()
	//handler := NewHandlerBasedOnMap()
	// 因为我们是一个链,所以我们把最后的业务逻辑处理,也作为一环
	var root Filter = handler.ServeHTTP
	// 从后往前把filter串起来
	for i := len(builders) - 1; i >= 0; i-- {
		b := builders[i]
		root = b(root)
	}
	res := &sdkHttpServer{
		Name: name,
		handler: handler,
		root: root,
		ctxPool: sync.Pool{New: func() interface {}{
			return newContext()
		}},
	}
	return res
}

func NewSdkHttpServerWithFilterNames(name string,
	filterNames...string) Server {
	// 这里取出来
	builders := make([]FilterBuilder, 0, len(filterNames))
	for _, n := range filterNames {
		b := GetFilterBuilder(n)
		builders = append(builders, b)
	}

	return NewSdkHttpServer(name, builders...)
}



================================================
FILE: pkg/static_resource.go
================================================
package web

import (
	"fmt"
	lru "github.com/hashicorp/golang-lru"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

type StaticResourceHandlerOption func(h *StaticResourceHandler)

type StaticResourceHandler struct {
	dir string
	pathPrefix string
	extensionContentTypeMap map[string]string

	// 缓存静态资源的限制
	cache *lru.Cache
	maxFileSize int
}

type fileCacheItem struct {
	fileName string
	fileSize int
	contentType string
	data []byte
}

func NewStaticResourceHandler(dir string, pathPrefix string,
	options...StaticResourceHandlerOption) *StaticResourceHandler {
	res := &StaticResourceHandler{
		dir: dir,
		pathPrefix: pathPrefix,
		extensionContentTypeMap: map[string]string{
			// 这里根据自己的需要不断添加
			"jpeg": "image/jpeg",
			"jpe": "image/jpeg",
			"jpg": "image/jpeg",
			"png": "image/png",
			"pdf": "image/pdf",
		},
	}

	for _, o := range options {
		o(res)
	}
	return res
}
// WithFileCache 静态文件将会被缓存
// maxFileSizeThreshold 超过这个大小的文件,就被认为是大文件,我们将不会缓存
// maxCacheFileCnt 最多缓存多少个文件
// 所以我们最多缓存 maxFileSizeThreshold * maxCacheFileCnt
func WithFileCache(maxFileSizeThreshold int, maxCacheFileCnt int) StaticResourceHandlerOption {
	return func(h *StaticResourceHandler) {
		c, err := lru.New(maxCacheFileCnt)
		if err != nil {
			fmt.Printf("could not create LRU, we won't cache static file")
		}
		h.maxFileSize = maxFileSizeThreshold
		h.cache = c
	}
}

func WithMoreExtension(extMap map[string]string) StaticResourceHandlerOption {
	return func(h *StaticResourceHandler) {
		for ext, contentType := range extMap {
			h.extensionContentTypeMap[ext] = contentType
		}
	}
}

func (h *StaticResourceHandler) ServeStaticResource(c *Context)  {
	req := strings.TrimPrefix(c.R.URL.Path, h.pathPrefix)
	if item, ok := h.readFileFromData(req); ok {
		fmt.Printf("read data from cache...")
		h.writeItemAsResponse(item, c.W)
		return
	}
	path := filepath.Join(h.dir, req)
	f, err := os.Open(path)
	if err != nil {
		c.W.WriteHeader(http.StatusInternalServerError)
		return
	}
	ext := getFileExt(f.Name())
	t, ok := h.extensionContentTypeMap[ext]
	if !ok {
		c.W.WriteHeader(http.StatusBadRequest)
		return
	}

	data, err := ioutil.ReadAll(f)
	if err != nil {
		c.W.WriteHeader(http.StatusInternalServerError)
		return
	}
	item := &fileCacheItem{
		fileSize: len(data),
		data: data,
		contentType: t,
		fileName: req,
	}

	h.cacheFile(item)
	h.writeItemAsResponse(item, c.W)

}

func (h *StaticResourceHandler) cacheFile(item *fileCacheItem) {
	if h.cache != nil && item.fileSize < h.maxFileSize {
		h.cache.Add(item.fileName, item)
	}
}

func (h *StaticResourceHandler) writeItemAsResponse(item *fileCacheItem, writer http.ResponseWriter) {
	writer.WriteHeader(http.StatusOK)
	writer.Header().Set("Content-Type", item.contentType)
	writer.Header().Set("Content-Length", fmt.Sprintf("%d", item.fileSize))
	_, _ = writer.Write(item.data)

}

func (h *StaticResourceHandler) readFileFromData(fileName string) (*fileCacheItem, bool) {
	if h.cache != nil {
		if item, ok := h.cache.Get(fileName); ok {
			return item.(*fileCacheItem), true
		}
	}
	return nil, false
}

func getFileExt(name string) string {
	index := strings.LastIndex(name, ".")
	if index == len(name) - 1{
		return ""
	}
	return name[index+1:]
}


================================================
FILE: pkg/tree_node.go
================================================
package web

import (
	"strings"
)

const (

	// 根节点,只有根用这个
	nodeTypeRoot = iota

	// *
	nodeTypeAny

	// 路径参数
	nodeTypeParam

	// 正则
	nodeTypeReg

	// 静态,即完全匹配
	nodeTypeStatic
)

const any = "*"

// matchFunc 承担两个职责,一个是判断是否匹配,一个是在匹配之后
// 将必要的数据写入到 Context
// 所谓必要的数据,这里基本上是指路径参数
type matchFunc func(path string, c *Context) bool

type node struct {
	children []*node

	// 如果这是叶子节点,
	// 那么匹配上之后就可以调用该方法
	handler   handlerFunc
	matchFunc matchFunc

	// 原始的 pattern。注意,它不是完整的pattern,
	// 而是匹配到这个节点的pattern
	pattern string
	nodeType int
}

// 静态节点
func newStaticNode(path string) *node {
	return &node{
		children: make([]*node, 0, 2),
		matchFunc: func(p string, c *Context) bool {
			return path == p && p != "*"
		},
		nodeType: nodeTypeStatic,
		pattern:  path,
	}
}


func newRootNode(method string) *node {
	return &node{
		children: make([]*node, 0, 2),
		matchFunc: func( p string, c *Context) bool {
			panic("never call me")
		},
		nodeType: nodeTypeRoot,
		pattern:  method,
	}
}

func newNode(path string) *node {
	if path == "*"{
		return newAnyNode()
	}
	if strings.HasPrefix(path, ":") {
		return newParamNode(path)
	}
	return newStaticNode(path)
}

// 通配符 * 节点
func newAnyNode() *node {
	return &node{
		// 因为我们不允许 * 后面还有节点,所以这里可以不用初始化
		//children: make([]*node, 0, 2),
		matchFunc: func(p string, c *Context) bool {
			return true
		},
		nodeType: nodeTypeAny,
		pattern:  any,
	}
}

// 路径参数节点
func newParamNode(path string) *node {
	paramName := path[1:]
	return &node{
		children: make([]*node, 0, 2),
		matchFunc: func(p string, c *Context) bool {
			if c != nil {
				c.PathParams[paramName] = p
			}
			// 如果自身是一个参数路由,
			// 然后又来一个通配符,我们认为是不匹配的
			return p != any
		},
		nodeType: nodeTypeParam,
		pattern:  path,
	}
}

// 正则节点
//func newRegNode(path string) *node {
//	// 依据你的规则拿到正则表达式
//	return &node{
//		children: make([]*node, 0, 2),
//		matchFunc: func(p string, c *Context) bool {
//			// 怎么写?
//		},
//		nodeType: nodeTypeParam,
//		pattern: path,
//	}
//}



================================================
FILE: pkg/tree_router.go
================================================
package web

import (
	"errors"
	"net/http"
	"sort"
	"strings"
)

var ErrorInvalidRouterPattern = errors.New("invalid router pattern")
var ErrorInvalidMethod = errors.New("invalid method")

type HandlerBasedOnTree struct {
	forest map[string]*node
}

var supportMethods = [4]string {
	http.MethodGet,
	http.MethodPost,
	http.MethodPut,
	http.MethodDelete,
}

func NewHandlerBasedOnTree() Handler {
	forest := make(map[string]*node, len(supportMethods))
	for _, m :=range supportMethods {
		forest[m] = newRootNode(m)
	}
	return &HandlerBasedOnTree{
		forest: forest,
	}
}

// ServeHTTP 就是从树里面找节点
// 找到了就执行
func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
	handler, found := h.findRouter(c.R.Method, c.R.URL.Path, c)
	if !found {
		c.W.WriteHeader(http.StatusNotFound)
		_, _ = c.W.Write([]byte("Not Found"))
		return
	}
	handler(c)
}

func (h *HandlerBasedOnTree) findRouter(method, path string, c *Context) (handlerFunc, bool) {
	// 去除头尾可能有的/,然后按照/切割成段
	paths := strings.Split(strings.Trim(path, "/"), "/")
	cur, ok := h.forest[method]
	if !ok {
		return nil, false
	}
	for _, p := range paths {
		// 从子节点里边找一个匹配到了当前 p 的节点
		matchChild, found := h.findMatchChild(cur, p, c)
		if !found {
			return nil, false
		}
		cur = matchChild
	}
	// 到这里,应该是找完了
	if cur.handler == nil {
		// 到达这里是因为这种场景
		// 比如说你注册了 /user/profile
		// 然后你访问 /user
		return nil, false
	}
	return cur.handler, true
}

// Route 就相当于往树里面插入节点
func (h *HandlerBasedOnTree) Route(method string, pattern string,
	handlerFunc handlerFunc) error {

	err := h.validatePattern(pattern)
	if err != nil {
		return err
	}

	// 将pattern按照URL的分隔符切割
	// 例如,/user/friends 将变成 [user, friends]
	// 将前后的/去掉,统一格式
	pattern = strings.Trim(pattern, "/")
	paths := strings.Split(pattern, "/")

	// 当前指向根节点
	cur, ok := h.forest[method]
	if !ok {
		return ErrorInvalidMethod
	}
	for index, path := range paths {

		// 从子节点里边找一个匹配到了当前 path 的节点
		matchChild, found := h.findMatchChild(cur, path, nil)
		// != nodeTypeAny 是考虑到 /order/* 和 /order/:id 这种注册顺序
		if found && matchChild.nodeType != nodeTypeAny {
			cur = matchChild
		} else {
			// 为当前节点根据
			h.createSubTree(cur, paths[index:], handlerFunc)
			return nil
		}
	}
	// 离开了循环,说明我们加入的是短路径,
	// 比如说我们先加入了 /order/detail
	// 再加入/order,那么会走到这里
	cur.handler = handlerFunc
	return nil
}

func (h *HandlerBasedOnTree) validatePattern(pattern string) error {
	// 校验 *,如果存在,必须在最后一个,并且它前面必须是/
	// 即我们只接受 /* 的存在,abc*这种是非法

	pos := strings.Index(pattern, "*")
	// 找到了 *
	if pos > 0 {
		// 必须是最后一个
		if pos != len(pattern) - 1 {
			return ErrorInvalidRouterPattern
		}
		if pattern[pos-1] != '/' {
			return ErrorInvalidRouterPattern
		}
	}
	return nil
}

func (h *HandlerBasedOnTree) findMatchChild(root *node,
	path string, c *Context) (*node, bool) {
	candidates := make([]*node, 0, 2)
	for _, child := range root.children {
		if child.matchFunc(path, c) {
			candidates = append(candidates, child)
		}
	}

	if len(candidates) == 0 {
		return nil, false
	}

	// type 也决定了它们的优先级
	sort.Slice(candidates, func(i, j int) bool {
		return candidates[i].nodeType < candidates[j].nodeType
	})
	return candidates[len(candidates) - 1], true
}

func (h *HandlerBasedOnTree) createSubTree(root *node, paths []string, handlerFn handlerFunc) {
	cur := root
	for _, path := range paths {
		nn := newNode(path)
		cur.children = append(cur.children, nn)
		cur = nn
	}
	cur.handler = handlerFn
}



================================================
FILE: pkg/tree_router_test.go
================================================
package web

import (
	"github.com/stretchr/testify/assert"
	"net/http"
	"reflect"
	"testing"
)

func TestHandlerBasedOnTree_Route(t *testing.T) {
	handler := NewHandlerBasedOnTree().(*HandlerBasedOnTree)
	// 要确认已经为支持的方法创建了节点
	assert.Equal(t, len(supportMethods), len(handler.forest))

	postNode := handler.forest[http.MethodPost]

	err := handler.Route(http.MethodPost, "/user", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 1, len(postNode.children))

	n := postNode.children[0]
	assert.NotNil(t, n)
	assert.Equal(t, "user", n.pattern)
	assert.NotNil(t, n.handler)
	assert.Empty(t, n.children)

	// 我们只有
	//  user -> profile
	err = handler.Route(http.MethodPost, "/user/profile", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 1, len(n.children))
	profileNode := n.children[0]
	assert.NotNil(t, profileNode)
	assert.Equal(t, "profile", profileNode.pattern)
	assert.NotNil(t, profileNode.handler)
	assert.Empty(t, profileNode.children)

	// 试试重复
	err = handler.Route(http.MethodPost, "/user", func(c *Context) {})
	assert.Nil(t, err)
	n = postNode.children[0]
	assert.NotNil(t, n)
	assert.Equal(t, "user", n.pattern)
	assert.NotNil(t, n.handler)
	// 有profile节点
	assert.Equal(t, 1, len(n.children))

	// 给 user 再加一个节点
	err = handler.Route(http.MethodPost, "/user/home", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(n.children))
	homeNode := n.children[1]
	assert.NotNil(t, homeNode)
	assert.Equal(t, "home", homeNode.pattern)
	assert.NotNil(t, homeNode.handler)
	assert.Empty(t, homeNode.children)

	// 添加 /order/detail
	err = handler.Route(http.MethodPost, "/order/detail", func(c *Context) {})
	assert.Equal(t, 2, len(postNode.children))
	orderNode := postNode.children[1]
	assert.NotNil(t, orderNode)
	assert.Equal(t, "order", orderNode.pattern)
	// 此刻我们只有/order/detail,但是没有/order
	assert.Nil(t, orderNode.handler)
	assert.Equal(t, 1, len(orderNode.children))

	orderDetailNode := orderNode.children[0]
	assert.NotNil(t, orderDetailNode)
	assert.Empty(t, orderDetailNode.children)
	assert.Equal(t, "detail", orderDetailNode.pattern)
	assert.NotNil(t, orderDetailNode.handler)

	// 加一个 /order
	err = handler.Route(http.MethodPost, "/order", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(postNode.children))
	orderNode = postNode.children[1]
	assert.Equal(t, "order", orderNode.pattern)
	// 此时我们有了 /order
	assert.NotNil(t, orderNode.handler)

	err = handler.Route(http.MethodPost, "/order/*", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(orderNode.children))
	orderWildcard := orderNode.children[1]
	assert.NotNil(t, orderWildcard)
	assert.NotNil(t, orderWildcard.handler)
	assert.Equal(t, "*", orderWildcard.pattern)

	err = handler.Route(http.MethodPost, "/order/*/checkout", func(c *Context) {})
	assert.Equal(t, ErrorInvalidRouterPattern, err)

	err = handler.Route(http.MethodConnect, "/order/checkout", func(c *Context) {})
	assert.Equal(t, ErrorInvalidMethod, err)

	err = handler.Route(http.MethodPost, "/order/:id", func(c *Context){})
	assert.Nil(t, err)
	// 这时候我们有/order/* 和 /order/:id
	// 因为我们并没有认为它们不兼容,而是/order/:id优先
	assert.Equal(t, 3, len(orderNode.children))
	orderParamNode := orderNode.children[2]
	assert.Equal(t, ":id", orderParamNode.pattern)

}

func TestHandlerBasedOnTree_findRouter(t *testing.T) {
	handler := NewHandlerBasedOnTree().(*HandlerBasedOnTree)
	_ = handler.Route(http.MethodPost, "/user", func(c *Context) {})
	ctx := NewContext(nil, nil)
	fn, found := handler.findRouter(http.MethodPost, "/user", ctx)
	assert.True(t, found)
	assert.NotNil(t, fn)
	_, found = handler.findRouter(http.MethodPost,"/user/profile", ctx)
	assert.False(t, found)

	_ = handler.Route(http.MethodPost, "/user/profile", func(c *Context) {})
	_, found = handler.findRouter(http.MethodPost, "/user/profile", ctx)
	assert.True(t, found)

	_, found = handler.findRouter(http.MethodPost, "/user", ctx)
	assert.True(t, found)

	var detailHandler handlerFunc = func(c *Context) {}
	_ = handler.Route(http.MethodPost, "/order/detail", detailHandler)
	_, found = handler.findRouter(http.MethodPost,"/order", ctx)
	assert.False(t, found)

	fn, found = handler.findRouter(http.MethodPost,"/order/detail", ctx)
	assert.True(t, found)
	assert.True(t, handlerFuncEquals(detailHandler, fn))

	var wildcardHandler handlerFunc = func(c *Context) {}
	_ = handler.Route(http.MethodPost, "/order/*", wildcardHandler)
	_, found = handler.findRouter(http.MethodPost,"/order", ctx)
	assert.False(t, found)

	fn, found = handler.findRouter(http.MethodPost,"/order/detail", ctx)
	assert.True(t, found)
	assert.True(t, handlerFuncEquals(detailHandler, fn))

	fn, found = handler.findRouter(http.MethodPost,"/order/checkout", ctx)
	assert.True(t, found)
	assert.True(t, handlerFuncEquals(wildcardHandler, fn))

	_, found = handler.findRouter(http.MethodGet,"/order/checkout", ctx)
	assert.False(t, found)

	// 参数路由
	handler.Route(http.MethodPost, "/order/*", wildcardHandler)
}

func handlerFuncEquals(hf1 handlerFunc, hf2 handlerFunc) bool {
	return reflect.ValueOf(hf1).Pointer() == reflect.ValueOf(hf2).Pointer()
}

================================================
FILE: pkg/v1/context.go
================================================
package webv1

import (
	"encoding/json"
	"io"
	"net/http"
)

type Context struct {
	W http.ResponseWriter
	R *http.Request
}

func (c *Context) ReadJson(data interface{}) error {
	body, err := io.ReadAll(c.R.Body)
	if err != nil {
		return err
	}
	return json.Unmarshal(body, data)
}
func (c *Context) OkJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusOK, data)
}

func (c *Context) SystemErrJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusInternalServerError, data)
}

func (c *Context) BadRequestJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusBadRequest, data)
}

func (c *Context) WriteJson(status int, data interface{}) error {
	bs, err := json.Marshal(data)
	if err != nil {
		return err
	}
	_, err = c.W.Write(bs)
	if err != nil {
		return err
	}
	c.W.WriteHeader(status)
	return nil
}

func NewContext(w http.ResponseWriter, r *http.Request) *Context {
	return &Context{
		W: w,
		R: r,
	}
}

================================================
FILE: pkg/v1/filter.go
================================================
package webv1

import (
	"fmt"
	"time"
)

type FilterBuilder func(next Filter) Filter

type Filter func(c *Context)

func MetricFilterBuilder(next Filter) Filter {
	return func(c *Context) {
		// 执行前的时间
		startTime := time.Now().UnixNano()
		next(c)
		// 执行后的时间
		endTime := time.Now().UnixNano()
		fmt.Printf("run time: %d \n", endTime-startTime)
	}
}

================================================
FILE: pkg/v1/handler.go
================================================
package webv1

type Handler interface {
	ServeHTTP(c *Context)
	Routable
}

type handlerFunc func(c *Context)

================================================
FILE: pkg/v1/map_router.go
================================================
package webv1

import (
	"fmt"
	"net/http"
	"sync"
)

// 一种常用的GO设计模式,
// 用于确保HandlerBasedOnMap肯定实现了这个接口
var _ Handler = &HandlerBasedOnMap{}


type HandlerBasedOnMap struct {
	handlers sync.Map
}

func (h *HandlerBasedOnMap) ServeHTTP(c *Context) {
	request := c.R
	key := h.key(request.Method, request.URL.Path)
	handler, ok := h.handlers.Load(key)
	if !ok {
		c.W.WriteHeader(http.StatusNotFound)
		_, _ = c.W.Write([]byte("not any router match"))
		return
	}

	handler.(handlerFunc)(c)
}

func (h *HandlerBasedOnMap) Route(method string, pattern string,
	handlerFunc handlerFunc) {
	key := h.key(method, pattern)
	h.handlers.Store(key, handlerFunc)
}

func (h *HandlerBasedOnMap) key(method string,
	path string) string {
	return fmt.Sprintf("%s#%s", method, path)
}

func NewHandlerBasedOnMap() *HandlerBasedOnMap {
	return &HandlerBasedOnMap{}
}


================================================
FILE: pkg/v1/server.go
================================================
package webv1

import (
	"net/http"
)

// Routable 可路由的
type Routable interface {
	// Route 设定一个路由,命中该路由的会执行handlerFunc的代码
	Route(method string, pattern string, handlerFunc handlerFunc)
}

// Server 是http server 的顶级抽象
type Server interface {
	Routable
	// Start 启动我们的服务器
	Start(address string) error
}

// sdkHttpServer 这个是基于 net/http 这个包实现的 http server
type sdkHttpServer struct {
	// Name server 的名字,给个标记,日志输出的时候用得上
	Name string
	handler Handler
	root Filter
}

func (s *sdkHttpServer) Route(method string, pattern string,
	handlerFunc handlerFunc) {
	s.handler.Route(method, pattern, handlerFunc)
}

func (s *sdkHttpServer) Start(address string) error {
	http.HandleFunc("/", func(writer http.ResponseWriter,
		request *http.Request) {
		c := NewContext(writer, request)
		s.root(c)
	})
	return http.ListenAndServe(address, nil)
}

func NewSdkHttpServer(name string, builders ...FilterBuilder) Server {

	// 改用我们的树
	handler := NewHandlerBasedOnTree()
	//handler := NewHandlerBasedOnMap()
	// 因为我们是一个链,所以我们把最后的业务逻辑处理,也作为一环
	var root Filter = handler.ServeHTTP
	// 从后往前把filter串起来
	for i := len(builders) - 1; i >= 0; i-- {
		b := builders[i]
		root = b(root)
	}
	res := &sdkHttpServer{
		Name: name,
		handler: handler,
		root: root,
	}
	return res
}



================================================
FILE: pkg/v1/tree_router.go
================================================
package webv1

import (
	"net/http"
	"strings"
)

type HandlerBasedOnTree struct {
	root *node

}

func NewHandlerBasedOnTree() Handler {
	return &HandlerBasedOnTree{
		root: &node{},
	}
}

// ServeHTTP 就是从树里面找节点
// 找到了就执行
func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
	handler, found := h.findRouter(c.R.URL.Path)
	if !found {
		c.W.WriteHeader(http.StatusNotFound)
		_, _ = c.W.Write([]byte("Not Found"))
		return
	}
	handler(c)
}

// 这个是不好测试的版本,可以尝试为这个写单元测试
// 会发现很难构造 request,也很难对 ResponseWriter做断言
//func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
//	url := strings.Trim(c.R.URL.Path, "/")
//	paths := strings.Split(url, "/")
//	cur := h.root
//	for _, path := range paths {
//		// 从子节点里边找一个匹配到了当前 path 的节点
//		matchChild, found := h.findMatchChild(cur, path)
//		if !found {
//			// 找不到匹配的路径,直接返回
//			c.W.WriteHeader(404)
//			_, _ = c.W.Write([]byte("Not Found"))
//			return
//		}
//		cur = matchChild
//	}
//	// 到这里,应该是找完了
//	if cur.handler == nil {
//		// 到达这里是因为这种场景
//		// 比如说你注册了 /user/profile
//		// 然后你访问 /user
//		c.W.WriteHeader(404)
//		_, _ = c.W.Write([]byte("Not Found"))
//		return
//	}
//	cur.handler(c)
//}

func (h *HandlerBasedOnTree) findRouter(path string) (handlerFunc, bool) {
	// 去除头尾可能有的/,然后按照/切割成段
	paths := strings.Split(strings.Trim(path, "/"), "/")
	cur := h.root
	for _, p := range paths {
		// 从子节点里边找一个匹配到了当前 path 的节点
		matchChild, found := h.findMatchChild(cur, p)
		if !found {
			return nil, false
		}
		cur = matchChild
	}
	// 到这里,应该是找完了
	if cur.handler == nil {
		// 到达这里是因为这种场景
		// 比如说你注册了 /user/profile
		// 然后你访问 /user
		return nil, false
	}
	return cur.handler, true
}

// Route 就相当于往树里面插入节点
func (h *HandlerBasedOnTree) Route(method string, pattern string,
	handlerFunc handlerFunc) {
	// 将pattern按照URL的分隔符切割
	// 例如,/user/friends 将变成 [user, friends]
	// 将前后的/去掉,统一格式
	pattern = strings.Trim(pattern, "/")
	paths := strings.Split(pattern, "/")
	// 当前指向根节点
	cur := h.root
	for index, path := range paths {
		// 从子节点里边找一个匹配到了当前 path 的节点
		matchChild, found := h.findMatchChild(cur, path)
		if found {
			cur = matchChild
		} else {
			// 为当前节点根据
			h.createSubTree(cur, paths[index:], handlerFunc)
			return
		}
	}
	// 离开了循环,说明我们加入的是短路径,
	// 比如说我们先加入了 /order/detail
	// 再加入/order,那么会走到这里
	cur.handler = handlerFunc
}

func (h *HandlerBasedOnTree) findMatchChild(root *node, path string) (*node, bool) {
	for _, child := range root.children {
		if child.path == path {
			return child, true
		}
	}
	return nil, false
}

func (h *HandlerBasedOnTree) createSubTree(root *node, paths []string, handlerFn handlerFunc) {
	cur := root
	for _, path := range paths {
		nn := newNode(path)
		cur.children = append(cur.children, nn)
		cur = nn
	}
	cur.handler = handlerFn
}

type node struct {
	path string
	children []*node

	// 如果这是叶子节点,
	// 那么匹配上之后就可以调用该方法
	handler handlerFunc
}

func newNode(path string) *node {
	return &node{
		path: path,
		children: make([]*node, 0, 2),
	}
}


================================================
FILE: pkg/v1/tree_router_test.go
================================================
package webv1

import (
	"github.com/stretchr/testify/assert"
	"net/http"
	"testing"
)

func TestHandlerBasedOnTree_Route(t *testing.T) {
	handler := NewHandlerBasedOnTree().(*HandlerBasedOnTree)
	assert.NotNil(t, handler.root)

	handler.Route(http.MethodPost, "/user", func(c *Context) {})

	// 开始做断言,这个时候我们应该确认,在根节点之下只有一个user节点
	assert.Equal(t, 1, len(handler.root.children))

	n := handler.root.children[0]
	assert.NotNil(t, n)
	assert.Equal(t, "user", n.path)
	assert.NotNil(t, n.handler)
	assert.Empty(t, n.children)

	// 我们只有
	//  user -> profile
	handler.Route(http.MethodPost, "/user/profile", func(c *Context) {})
	assert.Equal(t, 1, len(n.children))
	profileNode := n.children[0]
	assert.NotNil(t, profileNode)
	assert.Equal(t, "profile", profileNode.path)
	assert.NotNil(t, profileNode.handler)
	assert.Empty(t, profileNode.children)

	// 试试重复
	handler.Route(http.MethodPost, "/user", func(c *Context) {})
	n = handler.root.children[0]
	assert.NotNil(t, n)
	assert.Equal(t, "user", n.path)
	assert.NotNil(t, n.handler)
	// 有profile节点
	assert.Equal(t, 1, len(n.children))

	// 给 user 再加一个节点
	handler.Route(http.MethodPost, "/user/home", func(c *Context) {})
	assert.Equal(t, 2, len(n.children))
	homeNode := n.children[1]
	assert.NotNil(t, homeNode)
	assert.Equal(t, "home", homeNode.path)
	assert.NotNil(t, homeNode.handler)
	assert.Empty(t, homeNode.children)

	// 添加 /order/detail
	handler.Route(http.MethodPost, "/order/detail", func(c *Context) {})
	assert.Equal(t, 2, len(handler.root.children))
	orderNode := handler.root.children[1]
	assert.NotNil(t, orderNode)
	assert.Equal(t, "order", orderNode.path)
	// 此刻我们只有/order/detail,但是没有/order
	assert.Nil(t, orderNode.handler)
	assert.Equal(t, 1, len(orderNode.children))

	orderDetailNode := orderNode.children[0]
	assert.NotNil(t, orderDetailNode)
	assert.Empty(t, orderDetailNode.children)
	assert.Equal(t, "detail", orderDetailNode.path)
	assert.NotNil(t, orderDetailNode.handler)

	// 加一个 /order
	handler.Route(http.MethodPost, "/order", func(c *Context) {})
	assert.Equal(t, 2, len(handler.root.children))
	orderNode = handler.root.children[1]
	assert.Equal(t, "order", orderNode.path)
	// 此时我们有了 /order
	assert.NotNil(t, orderNode.handler)

}

func TestHandlerBasedOnTree_findRouter(t *testing.T) {
	handler := NewHandlerBasedOnTree().(*HandlerBasedOnTree)
	handler.Route(http.MethodPost, "/user", func(c *Context) {})
	_, found := handler.findRouter("/user")
	assert.True(t, found)
	_, found = handler.findRouter("/user/profile")
	assert.False(t, found)

	handler.Route(http.MethodPost, "/user/profile", func(c *Context) {})
	_, found = handler.findRouter("/user/profile")
	assert.True(t, found)

	_, found = handler.findRouter("/user")
	assert.True(t, found)

	handler.Route(http.MethodPost, "/order/detail", func(c *Context) {})
	_, found = handler.findRouter("/order")
	assert.False(t, found)

	_, found = handler.findRouter("/order/detail")
	assert.True(t, found)

	handler.Route(http.MethodPost, "/order", func(c *Context) {})
	_, found = handler.findRouter("/order")
	assert.True(t, found)
}

================================================
FILE: pkg/v2/context.go
================================================
package webv2

import (
	"encoding/json"
	"io"
	"net/http"
)

type Context struct {
	W http.ResponseWriter
	R *http.Request
}

func (c *Context) ReadJson(data interface{}) error {
	body, err := io.ReadAll(c.R.Body)
	if err != nil {
		return err
	}
	return json.Unmarshal(body, data)
}
func (c *Context) OkJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusOK, data)
}

func (c *Context) SystemErrJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusInternalServerError, data)
}

func (c *Context) BadRequestJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusBadRequest, data)
}

func (c *Context) WriteJson(status int, data interface{}) error {
	bs, err := json.Marshal(data)
	if err != nil {
		return err
	}
	_, err = c.W.Write(bs)
	if err != nil {
		return err
	}
	c.W.WriteHeader(status)
	return nil
}

func NewContext(w http.ResponseWriter, r *http.Request) *Context {
	return &Context{
		W: w,
		R: r,
	}
}

================================================
FILE: pkg/v2/filter.go
================================================
package webv2

import (
	"fmt"
	"time"
)

type FilterBuilder func(next Filter) Filter

type Filter func(c *Context)

func MetricFilterBuilder(next Filter) Filter {
	return func(c *Context) {
		// 执行前的时间
		startTime := time.Now().UnixNano()
		next(c)
		// 执行后的时间
		endTime := time.Now().UnixNano()
		fmt.Printf("run time: %d \n", endTime-startTime)
	}
}

================================================
FILE: pkg/v2/handler.go
================================================
package webv2

type Handler interface {
	ServeHTTP(c *Context)
	Routable
}

type handlerFunc func(c *Context)

================================================
FILE: pkg/v2/map_router.go
================================================
package webv2

import (
	"fmt"
	"net/http"
	"sync"
)

// 一种常用的GO设计模式,
// 用于确保HandlerBasedOnMap肯定实现了这个接口
var _ Handler = &HandlerBasedOnMap{}


type HandlerBasedOnMap struct {
	handlers sync.Map
}

func (h *HandlerBasedOnMap) ServeHTTP(c *Context) {
	request := c.R
	key := h.key(request.Method, request.URL.Path)
	handler, ok := h.handlers.Load(key)
	if !ok {
		c.W.WriteHeader(http.StatusNotFound)
		_, _ = c.W.Write([]byte("not any router match"))
		return
	}

	handler.(handlerFunc)(c)
}

func (h *HandlerBasedOnMap) Route(method string, pattern string,
	handlerFunc handlerFunc) error {
	key := h.key(method, pattern)
	h.handlers.Store(key, handlerFunc)
	return nil
}

func (h *HandlerBasedOnMap) key(method string,
	path string) string {
	return fmt.Sprintf("%s#%s", method, path)
}

func NewHandlerBasedOnMap() *HandlerBasedOnMap {
	return &HandlerBasedOnMap{}
}


================================================
FILE: pkg/v2/server.go
================================================
package webv2

import (
	"net/http"
)

// Routable 可路由的
type Routable interface {
	// Route 设定一个路由,命中该路由的会执行handlerFunc的代码
	Route(method string, pattern string, handlerFunc handlerFunc) error
}

// Server 是http server 的顶级抽象
type Server interface {
	Routable
	// Start 启动我们的服务器
	Start(address string) error
}

// sdkHttpServer 这个是基于 net/http 这个包实现的 http server
type sdkHttpServer struct {
	// Name server 的名字,给个标记,日志输出的时候用得上
	Name    string
	handler Handler
	root    Filter
}

func (s *sdkHttpServer) Route(method string, pattern string,
	handlerFunc handlerFunc) error {
	return s.handler.Route(method, pattern, handlerFunc)
}

func (s *sdkHttpServer) Start(address string) error {
	return http.ListenAndServe(address, s)
}

func (s *sdkHttpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request)  {
	c := NewContext(writer, request)
	s.root(c)
}

func NewSdkHttpServer(name string, builders ...FilterBuilder) Server {

	// 改用我们的树
	handler := NewHandlerBasedOnTree()
	//handler := NewHandlerBasedOnMap()
	// 因为我们是一个链,所以我们把最后的业务逻辑处理,也作为一环
	var root Filter = handler.ServeHTTP
	// 从后往前把filter串起来
	for i := len(builders) - 1; i >= 0; i-- {
		b := builders[i]
		root = b(root)
	}
	res := &sdkHttpServer{
		Name: name,
		handler: handler,
		root: root,
	}
	return res
}



================================================
FILE: pkg/v2/tree_router.go
================================================
package webv2

import (
	"errors"
	"net/http"
	"strings"
)

var ErrorInvalidRouterPattern = errors.New("invalid router pattern")

type HandlerBasedOnTree struct {
	root *node

}
var supportMethods = [4]string {http.MethodPost, http.MethodGet,
	http.MethodDelete, http.MethodPut}
func NewHandlerBasedOnTree() Handler {
	root := &node{
	}
	return &HandlerBasedOnTree{
		root: root,
	}
}

// ServeHTTP 就是从树里面找节点
// 找到了就执行
func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
	handler, found := h.findRouter(c.R.URL.Path)
	if !found {
		c.W.WriteHeader(http.StatusNotFound)
		_, _ = c.W.Write([]byte("Not Found"))
		return
	}
	handler(c)
}

// 这个是不好测试的版本,可以尝试为这个写单元测试
// 会发现很难构造 request,也很难对 ResponseWriter做断言
//func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
//	url := strings.Trim(c.R.URL.Path, "/")
//	paths := strings.Split(url, "/")
//	cur := h.root
//	for _, path := range paths {
//		// 从子节点里边找一个匹配到了当前 path 的节点
//		matchChild, found := h.findMatchChild(cur, path)
//		if !found {
//			// 找不到匹配的路径,直接返回
//			c.W.WriteHeader(404)
//			_, _ = c.W.Write([]byte("Not Found"))
//			return
//		}
//		cur = matchChild
//	}
//	// 到这里,应该是找完了
//	if cur.handler == nil {
//		// 到达这里是因为这种场景
//		// 比如说你注册了 /user/profile
//		// 然后你访问 /user
//		c.W.WriteHeader(404)
//		_, _ = c.W.Write([]byte("Not Found"))
//		return
//	}
//	cur.handler(c)
//}

func (h *HandlerBasedOnTree) findRouter(path string) (handlerFunc, bool) {
	// 去除头尾可能有的/,然后按照/切割成段
	paths := strings.Split(strings.Trim(path, "/"), "/")
	cur := h.root
	for _, p := range paths {
		// 从子节点里边找一个匹配到了当前 path 的节点
		matchChild, found := h.findMatchChild(cur, p)
		if !found {
			return nil, false
		}
		cur = matchChild
	}
	// 到这里,应该是找完了
	if cur.handler == nil {
		// 到达这里是因为这种场景
		// 比如说你注册了 /user/profile
		// 然后你访问 /user
		return nil, false
	}
	return cur.handler, true
}

// Route 就相当于往树里面插入节点
func (h *HandlerBasedOnTree) Route(method string, pattern string,
	handlerFunc handlerFunc) error {

	err := h.validatePattern(pattern)
	if err != nil {
		return err
	}

	// 将pattern按照URL的分隔符切割
	// 例如,/user/friends 将变成 [user, friends]
	// 将前后的/去掉,统一格式
	pattern = strings.Trim(pattern, "/")
	paths := strings.Split(pattern, "/")
	// 当前指向根节点
	cur := h.root
	for index, path := range paths {

		// 从子节点里边找一个匹配到了当前 path 的节点
		matchChild, found := h.findMatchChild(cur, path)
		if found {
			cur = matchChild
		} else {
			// 为当前节点根据
			h.createSubTree(cur, paths[index:], handlerFunc)
			return nil
		}
	}
	// 离开了循环,说明我们加入的是短路径,
	// 比如说我们先加入了 /order/detail
	// 再加入/order,那么会走到这里
	cur.handler = handlerFunc
	return nil
}

func (h *HandlerBasedOnTree) validatePattern(pattern string) error {
	// 校验 *,如果存在,必须在最后一个,并且它前面必须是/
	// 即我们只接受 /* 的存在,abc*这种是非法

	pos := strings.Index(pattern, "*")
	// 找到了 *
	if pos > 0 {
		// 必须是最后一个
		if pos != len(pattern) - 1 {
			return ErrorInvalidRouterPattern
		}
		if pattern[pos-1] != '/' {
			return ErrorInvalidRouterPattern
		}
	}
	return nil
}

func (h *HandlerBasedOnTree) findMatchChild(root *node, path string) (*node, bool) {
	var wildcardNode *node
	for _, child := range root.children {
		// 并不是 * 的节点命中了,直接返回
		// != * 是为了防止用户乱输入
		if child.path == path &&
			child.path != "*"{
			return child, true
		}
		// 命中了通配符的,我们看看后面还有没有更加详细的
		if child.path == "*" {
			wildcardNode = child
		}
	}
	return wildcardNode, wildcardNode != nil
}

func (h *HandlerBasedOnTree) createSubTree(root *node, paths []string, handlerFn handlerFunc) {
	cur := root
	for _, path := range paths {
		nn := newNode(path)
		cur.children = append(cur.children, nn)
		cur = nn
	}
	cur.handler = handlerFn
}

type node struct {
	path string
	children []*node

	// 如果这是叶子节点,
	// 那么匹配上之后就可以调用该方法
	handler handlerFunc
}

func newNode(path string) *node {
	return &node{
		path: path,
		children: make([]*node, 0, 2),
	}
}


================================================
FILE: pkg/v2/tree_router_test.go
================================================
package webv2

import (
	"github.com/stretchr/testify/assert"
	"net/http"
	"reflect"
	"testing"
)

func TestHandlerBasedOnTree_Route(t *testing.T) {
	handler := NewHandlerBasedOnTree().(*HandlerBasedOnTree)
	assert.NotNil(t, handler.root)

	err := handler.Route(http.MethodPost, "/user", func(c *Context) {})
	assert.Nil(t, err)
	// 开始做断言,这个时候我们应该确认,在根节点之下只有一个user节点
	assert.Equal(t, 1, len(handler.root.children))

	n := handler.root.children[0]
	assert.NotNil(t, n)
	assert.Equal(t, "user", n.path)
	assert.NotNil(t, n.handler)
	assert.Empty(t, n.children)

	// 我们只有
	//  user -> profile
	err = handler.Route(http.MethodPost, "/user/profile", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 1, len(n.children))
	profileNode := n.children[0]
	assert.NotNil(t, profileNode)
	assert.Equal(t, "profile", profileNode.path)
	assert.NotNil(t, profileNode.handler)
	assert.Empty(t, profileNode.children)

	// 试试重复
	err = handler.Route(http.MethodPost, "/user", func(c *Context) {})
	assert.Nil(t, err)
	n = handler.root.children[0]
	assert.NotNil(t, n)
	assert.Equal(t, "user", n.path)
	assert.NotNil(t, n.handler)
	// 有profile节点
	assert.Equal(t, 1, len(n.children))

	// 给 user 再加一个节点
	err = handler.Route(http.MethodPost, "/user/home", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(n.children))
	homeNode := n.children[1]
	assert.NotNil(t, homeNode)
	assert.Equal(t, "home", homeNode.path)
	assert.NotNil(t, homeNode.handler)
	assert.Empty(t, homeNode.children)

	// 添加 /order/detail
	err = handler.Route(http.MethodPost, "/order/detail", func(c *Context) {})
	assert.Equal(t, 2, len(handler.root.children))
	orderNode := handler.root.children[1]
	assert.NotNil(t, orderNode)
	assert.Equal(t, "order", orderNode.path)
	// 此刻我们只有/order/detail,但是没有/order
	assert.Nil(t, orderNode.handler)
	assert.Equal(t, 1, len(orderNode.children))

	orderDetailNode := orderNode.children[0]
	assert.NotNil(t, orderDetailNode)
	assert.Empty(t, orderDetailNode.children)
	assert.Equal(t, "detail", orderDetailNode.path)
	assert.NotNil(t, orderDetailNode.handler)

	// 加一个 /order
	err = handler.Route(http.MethodPost, "/order", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(handler.root.children))
	orderNode = handler.root.children[1]
	assert.Equal(t, "order", orderNode.path)
	// 此时我们有了 /order
	assert.NotNil(t, orderNode.handler)

	err = handler.Route(http.MethodPost, "/order/*", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(orderNode.children))
	orderWildcard := orderNode.children[1]
	assert.NotNil(t, orderWildcard)
	assert.NotNil(t, orderWildcard.handler)
	assert.Equal(t, "*", orderWildcard.path)

	err = handler.Route(http.MethodPost, "/order/*/checkout", func(c *Context) {})
	assert.NotNil(t, err)
}

func TestHandlerBasedOnTree_findRouter(t *testing.T) {
	handler := NewHandlerBasedOnTree().(*HandlerBasedOnTree)
	_ = handler.Route(http.MethodPost, "/user", func(c *Context) {})
	fn, found := handler.findRouter("/user")
	assert.True(t, found)
	assert.NotNil(t, fn)
	_, found = handler.findRouter("/user/profile")
	assert.False(t, found)

	_ = handler.Route(http.MethodPost, "/user/profile", func(c *Context) {})
	_, found = handler.findRouter("/user/profile")
	assert.True(t, found)

	_, found = handler.findRouter("/user")
	assert.True(t, found)

	var detailHandler handlerFunc = func(c *Context) {}
	_ = handler.Route(http.MethodPost, "/order/detail", detailHandler)
	_, found = handler.findRouter("/order")
	assert.False(t, found)

	fn, found = handler.findRouter("/order/detail")
	assert.True(t, found)
	assert.True(t, handlerFuncEquals(detailHandler, fn))

	var wildcardHandler handlerFunc = func(c *Context) {}
	_ = handler.Route(http.MethodPost, "/order/*", wildcardHandler)
	_, found = handler.findRouter("/order")
	assert.False(t, found)

	fn, found = handler.findRouter("/order/detail")
	assert.True(t, found)
	assert.True(t, handlerFuncEquals(detailHandler, fn))

	fn, found = handler.findRouter("/order/checkout")
	assert.True(t, found)
	assert.True(t, handlerFuncEquals(wildcardHandler, fn))
}

func handlerFuncEquals(hf1 handlerFunc, hf2 handlerFunc) bool {
	return reflect.ValueOf(hf1).Pointer() == reflect.ValueOf(hf2).Pointer()
}

================================================
FILE: pkg/v3/context.go
================================================
package webv3

import (
	"encoding/json"
	"io"
	"net/http"
)

type Context struct {
	W http.ResponseWriter
	R *http.Request
	PathParams map[string]string
}

func (c *Context) ReadJson(data interface{}) error {
	body, err := io.ReadAll(c.R.Body)
	if err != nil {
		return err
	}
	return json.Unmarshal(body, data)
}
func (c *Context) OkJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusOK, data)
}

func (c *Context) SystemErrJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusInternalServerError, data)
}

func (c *Context) BadRequestJson(data interface{}) error {
	// http 库里面提前定义好了各种响应码
	return c.WriteJson(http.StatusBadRequest, data)
}

func (c *Context) WriteJson(status int, data interface{}) error {
	c.W.WriteHeader(status)
	bs, err := json.Marshal(data)
	if err != nil {
		return err
	}
	_, err = c.W.Write(bs)
	if err != nil {
		return err
	}
	return nil
}

func NewContext(w http.ResponseWriter, r *http.Request) *Context {
	return &Context{
		W: w,
		R: r,
		// 一般路径参数都是一个,所以容量1就可以了
		PathParams: make(map[string]string, 1),
	}
}

================================================
FILE: pkg/v3/filter.go
================================================
package webv3

import (
	"fmt"
	"time"
)

type FilterBuilder func(next Filter) Filter

type Filter func(c *Context)

func MetricFilterBuilder(next Filter) Filter {
	return func(c *Context) {
		// 执行前的时间
		startTime := time.Now().UnixNano()
		next(c)
		// 执行后的时间
		endTime := time.Now().UnixNano()
		fmt.Printf("run time: %d \n", endTime-startTime)
	}
}

================================================
FILE: pkg/v3/handler.go
================================================
package webv3

type Handler interface {
	ServeHTTP(c *Context)
	Routable
}

type handlerFunc func(c *Context)

================================================
FILE: pkg/v3/map_router.go
================================================
package webv3

import (
	"fmt"
	"net/http"
	"sync"
)

// 一种常用的GO设计模式,
// 用于确保HandlerBasedOnMap肯定实现了这个接口
var _ Handler = &HandlerBasedOnMap{}


type HandlerBasedOnMap struct {
	handlers sync.Map
}

func (h *HandlerBasedOnMap) ServeHTTP(c *Context) {
	request := c.R
	key := h.key(request.Method, request.URL.Path)
	handler, ok := h.handlers.Load(key)
	if !ok {
		c.W.WriteHeader(http.StatusNotFound)
		_, _ = c.W.Write([]byte("not any router match"))
		return
	}

	handler.(handlerFunc)(c)
}

func (h *HandlerBasedOnMap) Route(method string, pattern string,
	handlerFunc handlerFunc) error {
	key := h.key(method, pattern)
	h.handlers.Store(key, handlerFunc)
	return nil
}

func (h *HandlerBasedOnMap) key(method string,
	path string) string {
	return fmt.Sprintf("%s#%s", method, path)
}

func NewHandlerBasedOnMap() *HandlerBasedOnMap {
	return &HandlerBasedOnMap{}
}


================================================
FILE: pkg/v3/server.go
================================================
package webv3

import (
	"net/http"
)

// Routable 可路由的
type Routable interface {
	// Route 设定一个路由,命中该路由的会执行handlerFunc的代码
	Route(method string, pattern string, handlerFunc handlerFunc) error
}

// Server 是http server 的顶级抽象
type Server interface {
	Routable
	// Start 启动我们的服务器
	Start(address string) error
}

// sdkHttpServer 这个是基于 net/http 这个包实现的 http server
type sdkHttpServer struct {
	// Name server 的名字,给个标记,日志输出的时候用得上
	Name    string
	handler Handler
	root    Filter
}

func (s *sdkHttpServer) Route(method string, pattern string,
	handlerFunc handlerFunc) error {
	return s.handler.Route(method, pattern, handlerFunc)
}

func (s *sdkHttpServer) Start(address string) error {
	return http.ListenAndServe(address, s)
}

func (s *sdkHttpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request)  {
	c := NewContext(writer, request)
	s.root(c)
}

func NewSdkHttpServer(name string, builders ...FilterBuilder) Server {

	// 改用我们的树
	handler := NewHandlerBasedOnTree()
	//handler := NewHandlerBasedOnMap()
	// 因为我们是一个链,所以我们把最后的业务逻辑处理,也作为一环
	var root Filter = handler.ServeHTTP
	// 从后往前把filter串起来
	for i := len(builders) - 1; i >= 0; i-- {
		b := builders[i]
		root = b(root)
	}
	res := &sdkHttpServer{
		Name: name,
		handler: handler,
		root: root,
	}
	return res
}



================================================
FILE: pkg/v3/tree_node.go
================================================
package webv3

import (
	"strings"
)

const (

	// 根节点,只有根用这个
	nodeTypeRoot = iota

	// *
	nodeTypeAny

	// 路径参数
	nodeTypeParam

	// 正则
	nodeTypeReg

	// 静态,即完全匹配
	nodeTypeStatic
)

const any = "*"

// matchFunc 承担两个职责,一个是判断是否匹配,一个是在匹配之后
// 将必要的数据写入到 Context
// 所谓必要的数据,这里基本上是指路径参数
type matchFunc func(path string, c *Context) bool

type node struct {
	children []*node

	// 如果这是叶子节点,
	// 那么匹配上之后就可以调用该方法
	handler   handlerFunc
	matchFunc matchFunc

	// 原始的 pattern。注意,它不是完整的pattern,
	// 而是匹配到这个节点的pattern
	pattern string
	nodeType int
}

// 静态节点
func newStaticNode(path string) *node {
	return &node{
		children: make([]*node, 0, 2),
		matchFunc: func(p string, c *Context) bool {
			return path == p && p != "*"
		},
		nodeType: nodeTypeStatic,
		pattern:  path,
	}
}


func newRootNode(method string) *node {
	return &node{
		children: make([]*node, 0, 2),
		matchFunc: func( p string, c *Context) bool {
			panic("never call me")
		},
		nodeType: nodeTypeRoot,
		pattern:  method,
	}
}

func newNode(path string) *node {
	if path == "*"{
		return newAnyNode()
	}
	if strings.HasPrefix(path, ":") {
		return newParamNode(path)
	}
	return newStaticNode(path)
}

// 通配符 * 节点
func newAnyNode() *node {
	return &node{
		// 因为我们不允许 * 后面还有节点,所以这里可以不用初始化
		//children: make([]*node, 0, 2),
		matchFunc: func(p string, c *Context) bool {
			return true
		},
		nodeType: nodeTypeAny,
		pattern:  any,
	}
}

// 路径参数节点
func newParamNode(path string) *node {
	paramName := path[1:]
	return &node{
		children: make([]*node, 0, 2),
		matchFunc: func(p string, c *Context) bool {
			if c != nil {
				c.PathParams[paramName] = p
			}
			// 如果自身是一个参数路由,
			// 然后又来一个通配符,我们认为是不匹配的
			return p != any
		},
		nodeType: nodeTypeParam,
		pattern:  path,
	}
}

// 正则节点
//func newRegNode(path string) *node {
//	// 依据你的规则拿到正则表达式
//	return &node{
//		children: make([]*node, 0, 2),
//		matchFunc: func(p string, c *Context) bool {
//			// 怎么写?
//		},
//		nodeType: nodeTypeParam,
//		pattern: path,
//	}
//}



================================================
FILE: pkg/v3/tree_router.go
================================================
package webv3

import (
	"errors"
	"net/http"
	"sort"
	"strings"
)

var ErrorInvalidRouterPattern = errors.New("invalid router pattern")
var ErrorInvalidMethod = errors.New("invalid method")

type HandlerBasedOnTree struct {
	forest map[string]*node
}

var supportMethods = [4]string {
	http.MethodGet,
	http.MethodPost,
	http.MethodPut,
	http.MethodDelete,
}

func NewHandlerBasedOnTree() Handler {
	forest := make(map[string]*node, len(supportMethods))
	for _, m :=range supportMethods {
		forest[m] = newRootNode(m)
	}
	return &HandlerBasedOnTree{
		forest: forest,
	}
}

// ServeHTTP 就是从树里面找节点
// 找到了就执行
func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
	handler, found := h.findRouter(c.R.Method, c.R.URL.Path, c)
	if !found {
		c.W.WriteHeader(http.StatusNotFound)
		_, _ = c.W.Write([]byte("Not Found"))
		return
	}
	handler(c)
}

func (h *HandlerBasedOnTree) findRouter(method, path string, c *Context) (handlerFunc, bool) {
	// 去除头尾可能有的/,然后按照/切割成段
	paths := strings.Split(strings.Trim(path, "/"), "/")
	cur, ok := h.forest[method]
	if !ok {
		return nil, false
	}
	for _, p := range paths {
		// 从子节点里边找一个匹配到了当前 p 的节点
		matchChild, found := h.findMatchChild(cur, p, c)
		if !found {
			return nil, false
		}
		cur = matchChild
	}
	// 到这里,应该是找完了
	if cur.handler == nil {
		// 到达这里是因为这种场景
		// 比如说你注册了 /user/profile
		// 然后你访问 /user
		return nil, false
	}
	return cur.handler, true
}

// Route 就相当于往树里面插入节点
func (h *HandlerBasedOnTree) Route(method string, pattern string,
	handlerFunc handlerFunc) error {

	err := h.validatePattern(pattern)
	if err != nil {
		return err
	}

	// 将pattern按照URL的分隔符切割
	// 例如,/user/friends 将变成 [user, friends]
	// 将前后的/去掉,统一格式
	pattern = strings.Trim(pattern, "/")
	paths := strings.Split(pattern, "/")

	// 当前指向根节点
	cur, ok := h.forest[method]
	if !ok {
		return ErrorInvalidMethod
	}
	for index, path := range paths {

		// 从子节点里边找一个匹配到了当前 path 的节点
		matchChild, found := h.findMatchChild(cur, path, nil)
		// != nodeTypeAny 是考虑到 /order/* 和 /order/:id 这种注册顺序
		if found && matchChild.nodeType != nodeTypeAny {
			cur = matchChild
		} else {
			// 为当前节点根据
			h.createSubTree(cur, paths[index:], handlerFunc)
			return nil
		}
	}
	// 离开了循环,说明我们加入的是短路径,
	// 比如说我们先加入了 /order/detail
	// 再加入/order,那么会走到这里
	cur.handler = handlerFunc
	return nil
}

func (h *HandlerBasedOnTree) validatePattern(pattern string) error {
	// 校验 *,如果存在,必须在最后一个,并且它前面必须是/
	// 即我们只接受 /* 的存在,abc*这种是非法

	pos := strings.Index(pattern, "*")
	// 找到了 *
	if pos > 0 {
		// 必须是最后一个
		if pos != len(pattern) - 1 {
			return ErrorInvalidRouterPattern
		}
		if pattern[pos-1] != '/' {
			return ErrorInvalidRouterPattern
		}
	}
	return nil
}

func (h *HandlerBasedOnTree) findMatchChild(root *node,
	path string, c *Context) (*node, bool) {
	candidates := make([]*node, 0, 2)
	for _, child := range root.children {
		if child.matchFunc(path, c) {
			candidates = append(candidates, child)
		}
	}

	if len(candidates) == 0 {
		return nil, false
	}

	// type 也决定了它们的优先级
	sort.Slice(candidates, func(i, j int) bool {
		return candidates[i].nodeType < candidates[j].nodeType
	})
	return candidates[len(candidates) - 1], true
}

func (h *HandlerBasedOnTree) createSubTree(root *node, paths []string, handlerFn handlerFunc) {
	cur := root
	for _, path := range paths {
		nn := newNode(path)
		cur.children = append(cur.children, nn)
		cur = nn
	}
	cur.handler = handlerFn
}



================================================
FILE: pkg/v3/tree_router_test.go
================================================
package webv3

import (
	"github.com/stretchr/testify/assert"
	"net/http"
	"reflect"
	"testing"
)

func TestHandlerBasedOnTree_Route(t *testing.T) {
	handler := NewHandlerBasedOnTree().(*HandlerBasedOnTree)
	// 要确认已经为支持的方法创建了节点
	assert.Equal(t, len(supportMethods), len(handler.forest))

	postNode := handler.forest[http.MethodPost]

	err := handler.Route(http.MethodPost, "/user", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 1, len(postNode.children))

	n := postNode.children[0]
	assert.NotNil(t, n)
	assert.Equal(t, "user", n.pattern)
	assert.NotNil(t, n.handler)
	assert.Empty(t, n.children)

	// 我们只有
	//  user -> profile
	err = handler.Route(http.MethodPost, "/user/profile", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 1, len(n.children))
	profileNode := n.children[0]
	assert.NotNil(t, profileNode)
	assert.Equal(t, "profile", profileNode.pattern)
	assert.NotNil(t, profileNode.handler)
	assert.Empty(t, profileNode.children)

	// 试试重复
	err = handler.Route(http.MethodPost, "/user", func(c *Context) {})
	assert.Nil(t, err)
	n = postNode.children[0]
	assert.NotNil(t, n)
	assert.Equal(t, "user", n.pattern)
	assert.NotNil(t, n.handler)
	// 有profile节点
	assert.Equal(t, 1, len(n.children))

	// 给 user 再加一个节点
	err = handler.Route(http.MethodPost, "/user/home", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(n.children))
	homeNode := n.children[1]
	assert.NotNil(t, homeNode)
	assert.Equal(t, "home", homeNode.pattern)
	assert.NotNil(t, homeNode.handler)
	assert.Empty(t, homeNode.children)

	// 添加 /order/detail
	err = handler.Route(http.MethodPost, "/order/detail", func(c *Context) {})
	assert.Equal(t, 2, len(postNode.children))
	orderNode := postNode.children[1]
	assert.NotNil(t, orderNode)
	assert.Equal(t, "order", orderNode.pattern)
	// 此刻我们只有/order/detail,但是没有/order
	assert.Nil(t, orderNode.handler)
	assert.Equal(t, 1, len(orderNode.children))

	orderDetailNode := orderNode.children[0]
	assert.NotNil(t, orderDetailNode)
	assert.Empty(t, orderDetailNode.children)
	assert.Equal(t, "detail", orderDetailNode.pattern)
	assert.NotNil(t, orderDetailNode.handler)

	// 加一个 /order
	err = handler.Route(http.MethodPost, "/order", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(postNode.children))
	orderNode = postNode.children[1]
	assert.Equal(t, "order", orderNode.pattern)
	// 此时我们有了 /order
	assert.NotNil(t, orderNode.handler)

	err = handler.Route(http.MethodPost, "/order/*", func(c *Context) {})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(orderNode.children))
	orderWildcard := orderNode.children[1]
	assert.NotNil(t, orderWildcard)
	assert.NotNil(t, orderWildcard.handler)
	assert.Equal(t, "*", orderWildcard.pattern)

	err = handler.Route(http.MethodPost, "/order/*/checkout", func(c *Context) {})
	assert.Equal(t, ErrorInvalidRouterPattern, err)

	err = handler.Route(http.MethodConnect, "/order/checkout", func(c *Context) {})
	assert.Equal(t, ErrorInvalidMethod, err)

	err = handler.Route(http.MethodPost, "/order/:id", func(c *Context){})
	assert.Nil(t, err)
	// 这时候我们有/order/* 和 /order/:id
	// 因为我们并没有认为它们不兼容,而是/order/:id优先
	assert.Equal(t, 3, len(orderNode.children))
	orderParamNode := orderNode.children[2]
	assert.Equal(t, ":id", orderParamNode.pattern)

}

func TestHandlerBasedOnTree_findRouter(t *testing.T) {
	handler := NewHandlerBasedOnTree().(*HandlerBasedOnTree)
	_ = handler.Route(http.MethodPost, "/user", func(c *Context) {})
	ctx := NewContext(nil, nil)
	fn, found := handler.findRouter(http.MethodPost, "/user", ctx)
	assert.True(t, found)
	assert.NotNil(t, fn)
	_, found = handler.findRouter(http.MethodPost,"/user/profile", ctx)
	assert.False(t, found)

	_ = handler.Route(http.MethodPost, "/user/profile", func(c *Context) {})
	_, found = handler.findRouter(http.MethodPost, "/user/profile", ctx)
	assert.True(t, found)

	_, found = handler.findRouter(http.MethodPost, "/user", ctx)
	assert.True(t, found)

	var detailHandler handlerFunc = func(c *Context) {}
	_ = handler.Route(http.MethodPost, "/order/detail", detailHandler)
	_, found = handler.findRouter(http.MethodPost,"/order", ctx)
	assert.False(t, found)

	fn, found = handler.findRouter(http.MethodPost,"/order/detail", ctx)
	assert.True(t, found)
	assert.True(t, handlerFuncEquals(detailHandler, fn))

	var wildcardHandler handlerFunc = func(c *Context) {}
	_ = handler.Route(http.MethodPost, "/order/*", wildcardHandler)
	_, found = handler.findRouter(http.MethodPost,"/order", ctx)
	assert.False(t, found)

	fn, found = handler.findRouter(http.MethodPost,"/order/detail", ctx)
	assert.True(t, found)
	assert.True(t, handlerFuncEquals(detailHandler, fn))

	fn, found = handler.findRouter(http.MethodPost,"/order/checkout", ctx)
	assert.True(t, found)
	assert.True(t, handlerFuncEquals(wildcardHandler, fn))

	_, found = handler.findRouter(http.MethodGet,"/order/checkout", ctx)
	assert.False(t, found)

	// 参数路由
	handler.Route(http.MethodPost, "/order/*", wildcardHandler)
}

func handlerFuncEquals(hf1 handlerFunc, hf2 handlerFunc) bool {
	return reflect.ValueOf(hf1).Pointer() == reflect.ValueOf(hf2).Pointer()
}
Download .txt
gitextract_k1h4qj8l/

├── .gitignore
├── LICENSE
├── README.md
├── demo/
│   ├── filters/
│   │   └── filter.go
│   └── user.go
├── examples/
│   ├── first_lesson/
│   │   ├── afterclass/
│   │   │   ├── fibonacci.go
│   │   │   ├── fmt.go
│   │   │   └── slice.go
│   │   ├── array_slice/
│   │   │   ├── array.go
│   │   │   └── slice.go
│   │   ├── fmt/
│   │   │   └── fmt.go
│   │   ├── for/
│   │   │   └── for.go
│   │   ├── func_dec/
│   │   │   └── funcs.go
│   │   ├── if_else/
│   │   │   └── ifelse.go
│   │   ├── package_dec/
│   │   │   ├── multi_same/
│   │   │   │   ├── a.go
│   │   │   │   └── b.go
│   │   │   └── not_same/
│   │   │       └── not_same.go
│   │   ├── switch/
│   │   │   └── switch.go
│   │   ├── types/
│   │   │   ├── rune.go
│   │   │   └── string.go
│   │   └── var_and_const/
│   │       ├── assignment.go
│   │       ├── const.go
│   │       ├── var.go
│   │       └── var_wrong.go
│   ├── forth_lesson/
│   │   ├── atomic/
│   │   │   └── atomic.go
│   │   ├── channel/
│   │   │   └── channel.go
│   │   ├── context/
│   │   │   └── context.go
│   │   ├── init/
│   │   │   ├── init_order.go
│   │   │   └── multi_init.go
│   │   ├── select/
│   │   │   └── select.go
│   │   └── static_resource/
│   │       └── file_server.go
│   ├── second_lesson/
│   │   ├── afterclass/
│   │   │   ├── set.go
│   │   │   └── tree.go
│   │   ├── composition/
│   │   │   ├── composition.go
│   │   │   └── no_over_write.go
│   │   ├── http/
│   │   │   └── request_body.go
│   │   ├── map/
│   │   │   └── map.go
│   │   ├── server_context/
│   │   │   └── signup.go
│   │   └── struct/
│   │       ├── intf.go
│   │       ├── pointer.go
│   │       ├── receiver.go
│   │       ├── self_ref.go
│   │       ├── struct.go
│   │       ├── type_a_b.go
│   │       └── type_a_et_b.go
│   └── third_lesson/
│       ├── closure/
│       │   └── closure.go
│       ├── defer/
│       │   └── defer.go
│       ├── errors/
│       │   ├── error.go
│       │   └── panic.go
│       ├── goroutine/
│       │   └── goroutine.go
│       └── sync/
│           ├── map.go
│           ├── mutex.go
│           ├── once.go
│           ├── pool.go
│           └── wait_group.go
├── go.mod
├── go.sum
├── main.go
├── onclass/
│   └── main.go
└── pkg/
    ├── context.go
    ├── filter.go
    ├── graceful_shutdown.go
    ├── graceful_shutdown_signal_darwin.go
    ├── graceful_shutdown_signal_linux.go
    ├── graceful_shutdown_signal_windows.go
    ├── handler.go
    ├── hook.go
    ├── hook_test.go
    ├── map_router.go
    ├── server.go
    ├── static_resource.go
    ├── tree_node.go
    ├── tree_router.go
    ├── tree_router_test.go
    ├── v1/
    │   ├── context.go
    │   ├── filter.go
    │   ├── handler.go
    │   ├── map_router.go
    │   ├── server.go
    │   ├── tree_router.go
    │   └── tree_router_test.go
    ├── v2/
    │   ├── context.go
    │   ├── filter.go
    │   ├── handler.go
    │   ├── map_router.go
    │   ├── server.go
    │   ├── tree_router.go
    │   └── tree_router_test.go
    └── v3/
        ├── context.go
        ├── filter.go
        ├── handler.go
        ├── map_router.go
        ├── server.go
        ├── tree_node.go
        ├── tree_router.go
        └── tree_router_test.go
Download .txt
SYMBOL INDEX (342 symbols across 85 files)

FILE: demo/filters/filter.go
  function init (line 8) | func init() {
  function myFilterBuilder (line 12) | func myFilterBuilder(next web.Filter) web.Filter {

FILE: demo/user.go
  function SignUp (line 9) | func SignUp(c *web.Context) {
  function SlowService (line 26) | func SlowService(c *web.Context) {
  type signUpReq (line 33) | type signUpReq struct
  type commonResponse (line 39) | type commonResponse struct

FILE: examples/first_lesson/afterclass/fibonacci.go
  function main (line 3) | func main() {
  function fibonacci (line 7) | func fibonacci(n int) int {

FILE: examples/first_lesson/afterclass/fmt.go
  function main (line 3) | func main() {
  function printNumWith2 (line 8) | func printNumWith2(float642 float64) string {
  function printBytes (line 12) | func printBytes(data []byte) string {

FILE: examples/first_lesson/afterclass/slice.go
  function main (line 3) | func main() {
  function Add (line 25) | func Add(s []int, index int, value int) []int {
  function Delete (line 30) | func Delete(s []int, index int) []int {

FILE: examples/first_lesson/array_slice/array.go
  function main (line 5) | func main() {

FILE: examples/first_lesson/array_slice/slice.go
  function main (line 5) | func main() {
  function SubSlice (line 34) | func SubSlice() {
  function ShareSlice (line 46) | func ShareSlice() {

FILE: examples/first_lesson/fmt/fmt.go
  function main (line 5) | func main() {
  function replaceHolder (line 18) | func replaceHolder() {
  type user (line 29) | type user struct

FILE: examples/first_lesson/for/for.go
  function main (line 5) | func main() {
  function ForLoop (line 11) | func ForLoop()  {
  function ForI (line 25) | func ForI()  {
  function ForR (line 33) | func ForR()  {

FILE: examples/first_lesson/func_dec/funcs.go
  function main (line 5) | func main() {
  function Fun0 (line 23) | func Fun0(name string) string {
  function Fun1 (line 28) | func Fun1(a string, b int) (int, string) {
  function Fun2 (line 34) | func Fun2(a string, b string) (age int, name string) {
  function Fun3 (line 42) | func Fun3(a, b, c string, abc, bcd int, p string) (d, e int, g string) {
  function Fun4 (line 51) | func Fun4(a string, b int, names...string)  {

FILE: examples/first_lesson/if_else/ifelse.go
  function main (line 5) | func main() {
  function Young (line 13) | func Young(age int) {
  function IfUsingNewVariable (line 22) | func IfUsingNewVariable(start int, end int) {

FILE: examples/first_lesson/switch/switch.go
  function main (line 5) | func main() {
  function ChooseFruit (line 11) | func ChooseFruit(fruit string) {

FILE: examples/first_lesson/types/rune.go
  function main (line 3) | func main() {

FILE: examples/first_lesson/types/string.go
  function main (line 5) | func main() {

FILE: examples/first_lesson/var_and_const/assignment.go
  function main (line 3) | func main() {

FILE: examples/first_lesson/var_and_const/const.go
  constant internal (line 3) | internal = "包内可访问"
  constant External (line 4) | External = "包外可访问"
  function main (line 6) | func main() {

FILE: examples/first_lesson/var_and_const/var.go
  function main (line 15) | func main() {

FILE: examples/first_lesson/var_and_const/var_wrong.go
  function main (line 5) | func main() {

FILE: examples/forth_lesson/atomic/atomic.go
  function main (line 6) | func main() {

FILE: examples/forth_lesson/channel/channel.go
  function main (line 8) | func main() {
  function channelWithCache (line 13) | func channelWithCache()  {
  function channelWithoutCache (line 32) | func channelWithoutCache() {

FILE: examples/forth_lesson/context/context.go
  function main (line 9) | func main() {
  function WithTimeout (line 16) | func WithTimeout() {
  function WithCancel (line 27) | func WithCancel() {
  function WithDeadline (line 40) | func WithDeadline() {
  function WithValue (line 53) | func WithValue() {

FILE: examples/forth_lesson/init/init_order.go
  function init (line 3) | func init() {
  function initBeforeSomething (line 11) | func initBeforeSomething()  {
  function initSomething (line 15) | func initSomething()  {
  function initAfterSomething (line 19) | func initAfterSomething()  {

FILE: examples/forth_lesson/init/multi_init.go
  function init (line 3) | func init() {
  function init (line 7) | func init() {

FILE: examples/forth_lesson/select/select.go
  function main (line 8) | func main() {
  function Select (line 14) | func Select() {

FILE: examples/forth_lesson/static_resource/file_server.go
  function main (line 5) | func main() {

FILE: examples/second_lesson/afterclass/set.go
  type Set (line 3) | type Set interface

FILE: examples/second_lesson/afterclass/tree.go
  type Tree (line 3) | type Tree interface
  type binaryTree (line 8) | type binaryTree struct
  type mutliWayTree (line 13) | type mutliWayTree struct

FILE: examples/second_lesson/composition/composition.go
  function main (line 5) | func main() {
  type Swimming (line 10) | type Swimming interface
  type Duck (line 14) | type Duck interface
  type Base (line 20) | type Base struct
    method SayHello (line 42) | func (b *Base) SayHello() {
  type Concrete1 (line 24) | type Concrete1 struct
    method SayHello (line 32) | func (c Concrete1) SayHello() {
  type Concrete2 (line 28) | type Concrete2 struct

FILE: examples/second_lesson/composition/no_over_write.go
  function main (line 5) | func main() {
  type Parent (line 13) | type Parent struct
    method SayHello (line 17) | func (p Parent) SayHello() {
    method Name (line 21) | func (p Parent) Name() string {
  type Son (line 25) | type Son struct
    method Name (line 30) | func (s Son) Name() string {

FILE: examples/second_lesson/http/request_body.go
  function home (line 10) | func home(w http.ResponseWriter, r *http.Request)  {
  function readBodyOnce (line 14) | func readBodyOnce(w http.ResponseWriter, r *http.Request)  {
  function getBodyIsNil (line 35) | func getBodyIsNil(w http.ResponseWriter, r *http.Request) {
  function queryParams (line 43) | func queryParams(w http.ResponseWriter, r *http.Request) {
  function wholeUrl (line 48) | func wholeUrl(w http.ResponseWriter, r *http.Request)  {
  function header (line 53) | func header(w http.ResponseWriter, r *http.Request) {
  function form (line 57) | func form(w http.ResponseWriter, r *http.Request)  {
  function main (line 66) | func main() {

FILE: examples/second_lesson/map/map.go
  function main (line 5) | func main() {

FILE: examples/second_lesson/server_context/signup.go
  function SignUpWithoutContext (line 12) | func SignUpWithoutContext(w http.ResponseWriter, r *http.Request) {
  function SignUpWithoutWrite (line 31) | func SignUpWithoutWrite(w http.ResponseWriter, r *http.Request) {
  type signUpReq (line 48) | type signUpReq struct
  type commonResponse (line 54) | type commonResponse struct

FILE: examples/second_lesson/struct/intf.go
  type animal (line 4) | type animal interface
  type Duck (line 13) | type Duck interface

FILE: examples/second_lesson/struct/pointer.go
  function main (line 5) | func main() {

FILE: examples/second_lesson/struct/receiver.go
  function main (line 5) | func main() {
  type User (line 30) | type User struct
    method ChangeName (line 36) | func (u User) ChangeName(newName string)  {
    method ChangeAge (line 41) | func (u *User) ChangeAge(newAge int) {

FILE: examples/second_lesson/struct/self_ref.go
  function main (line 3) | func main() {
  type Node (line 7) | type Node struct
  type NodeNode (line 20) | type NodeNode struct

FILE: examples/second_lesson/struct/struct.go
  function main (line 5) | func main() {
  type ToyDuck (line 45) | type ToyDuck struct
    method Swim (line 50) | func (t *ToyDuck) Swim() {

FILE: examples/second_lesson/struct/type_a_b.go
  function main (line 5) | func main() {
  type FakeFish (line 27) | type FakeFish
    method FakeSwim (line 29) | func (f FakeFish) FakeSwim() {
  type StrongFakeFish (line 34) | type StrongFakeFish
    method Swim (line 36) | func (f StrongFakeFish) Swim() {
  type Fish (line 40) | type Fish struct
    method Swim (line 43) | func (f Fish) Swim() {

FILE: examples/second_lesson/struct/type_a_et_b.go
  function main (line 5) | func main() {
  type News (line 12) | type News struct
    method Report (line 16) | func (d News) Report() {

FILE: examples/third_lesson/closure/closure.go
  function main (line 8) | func main() {
  function ReturnClosure (line 22) | func ReturnClosure(name string) func() string {
  function Delay (line 28) | func Delay() {

FILE: examples/third_lesson/defer/defer.go
  function main (line 5) | func main() {

FILE: examples/third_lesson/errors/error.go
  function main (line 8) | func main() {
  type MyError (line 15) | type MyError struct
    method Error (line 18) | func (m *MyError) Error() string {
  function ErrorsPkg (line 22) | func ErrorsPkg()  {

FILE: examples/third_lesson/errors/panic.go
  function main (line 5) | func main() {

FILE: examples/third_lesson/goroutine/goroutine.go
  function main (line 8) | func main() {
  function GoRoutine (line 12) | func GoRoutine() {

FILE: examples/third_lesson/sync/map.go
  function main (line 8) | func main() {

FILE: examples/third_lesson/sync/mutex.go
  function Mutex (line 9) | func Mutex() {
  function RwMutex (line 15) | func RwMutex()  {
  function Failed1 (line 26) | func Failed1()  {
  function Failed2 (line 37) | func Failed2()  {

FILE: examples/third_lesson/sync/once.go
  function main (line 8) | func main() {
  function PrintOnce (line 17) | func PrintOnce() {

FILE: examples/third_lesson/sync/pool.go
  function main (line 5) | func main() {
  type user (line 22) | type user struct
    method Reset (line 29) | func (u *user) Reset(name string, email string)  {

FILE: examples/third_lesson/sync/wait_group.go
  function main (line 8) | func main() {

FILE: main.go
  function home (line 13) | func home(w http.ResponseWriter, r *http.Request) {
  function user (line 17) | func user(w http.ResponseWriter, r *http.Request) {
  function createUser (line 21) | func createUser(w http.ResponseWriter, r *http.Request) {
  function order (line 25) | func order(w http.ResponseWriter, r *http.Request)  {
  function main (line 29) | func main() {

FILE: onclass/main.go
  function home (line 8) | func home(w http.ResponseWriter, r *http.Request) {
  function user (line 12) | func user(w http.ResponseWriter, r *http.Request) {
  function createUser (line 16) | func createUser(w http.ResponseWriter, r *http.Request) {
  function order (line 20) | func order(w http.ResponseWriter, r *http.Request)  {
  function main (line 25) | func main() {
  type Server (line 33) | type Server interface
  type sdkHttpServer (line 38) | type sdkHttpServer struct

FILE: pkg/context.go
  type Context (line 10) | type Context struct
    method ReadJson (line 16) | func (c *Context) ReadJson(data interface{}) error {
    method OkJson (line 23) | func (c *Context) OkJson(data interface{}) error {
    method SystemErrJson (line 28) | func (c *Context) SystemErrJson(data interface{}) error {
    method BadRequestJson (line 33) | func (c *Context) BadRequestJson(data interface{}) error {
    method WriteJson (line 38) | func (c *Context) WriteJson(status int, data interface{}) error {
    method Reset (line 66) | func (c *Context) Reset(w http.ResponseWriter, r *http.Request) {
  function NewContext (line 51) | func NewContext(w http.ResponseWriter, r *http.Request) *Context {
  function newContext (line 60) | func newContext() *Context {

FILE: pkg/filter.go
  type FilterBuilder (line 8) | type FilterBuilder
  type Filter (line 10) | type Filter
  function MetricFilterBuilder (line 12) | func MetricFilterBuilder(next Filter) Filter {
  function RegisterFilter (line 24) | func RegisterFilter(name string, builder FilterBuilder)  {
  function GetFilterBuilder (line 30) | func GetFilterBuilder(name string) FilterBuilder {

FILE: pkg/graceful_shutdown.go
  type GracefulShutdown (line 16) | type GracefulShutdown struct
    method ShutdownFilterBuilder (line 42) | func (g *GracefulShutdown) ShutdownFilterBuilder(next Filter) Filter {
    method RejectNewRequestAndWaiting (line 61) | func (g *GracefulShutdown) RejectNewRequestAndWaiting(ctx context.Cont...
  function NewGracefulShutdown (line 26) | func NewGracefulShutdown() *GracefulShutdown {
  function WaitForShutdown (line 85) | func WaitForShutdown(hooks...Hook) {

FILE: pkg/handler.go
  type Handler (line 3) | type Handler interface
  type handlerFunc (line 8) | type handlerFunc

FILE: pkg/hook.go
  type Hook (line 13) | type Hook
  function BuildCloseServerHook (line 18) | func BuildCloseServerHook(servers ...Server) Hook {

FILE: pkg/hook_test.go
  function TestBuildCloseServerHook (line 10) | func TestBuildCloseServerHook(t *testing.T) {

FILE: pkg/map_router.go
  type HandlerBasedOnMap (line 14) | type HandlerBasedOnMap struct
    method ServeHTTP (line 18) | func (h *HandlerBasedOnMap) ServeHTTP(c *Context) {
    method Route (line 31) | func (h *HandlerBasedOnMap) Route(method string, pattern string,
    method key (line 38) | func (h *HandlerBasedOnMap) key(method string,
  function NewHandlerBasedOnMap (line 43) | func NewHandlerBasedOnMap() *HandlerBasedOnMap {

FILE: pkg/server.go
  type Routable (line 12) | type Routable interface
  type Server (line 18) | type Server interface
  type sdkHttpServer (line 27) | type sdkHttpServer struct
    method Route (line 35) | func (s *sdkHttpServer) Route(method string, pattern string,
    method Start (line 40) | func (s *sdkHttpServer) Start(address string) error {
    method ServeHTTP (line 44) | func (s *sdkHttpServer) ServeHTTP(writer http.ResponseWriter, request ...
    method Shutdown (line 53) | func (s *sdkHttpServer) Shutdown(ctx context.Context) error {
  function NewSdkHttpServer (line 62) | func NewSdkHttpServer(name string, builders ...FilterBuilder) Server {
  function NewSdkHttpServerWithFilterNames (line 85) | func NewSdkHttpServerWithFilterNames(name string,

FILE: pkg/static_resource.go
  type StaticResourceHandlerOption (line 13) | type StaticResourceHandlerOption
  type StaticResourceHandler (line 15) | type StaticResourceHandler struct
    method ServeStaticResource (line 75) | func (h *StaticResourceHandler) ServeStaticResource(c *Context)  {
    method cacheFile (line 112) | func (h *StaticResourceHandler) cacheFile(item *fileCacheItem) {
    method writeItemAsResponse (line 118) | func (h *StaticResourceHandler) writeItemAsResponse(item *fileCacheIte...
    method readFileFromData (line 126) | func (h *StaticResourceHandler) readFileFromData(fileName string) (*fi...
  type fileCacheItem (line 25) | type fileCacheItem struct
  function NewStaticResourceHandler (line 32) | func NewStaticResourceHandler(dir string, pathPrefix string,
  function WithFileCache (line 56) | func WithFileCache(maxFileSizeThreshold int, maxCacheFileCnt int) Static...
  function WithMoreExtension (line 67) | func WithMoreExtension(extMap map[string]string) StaticResourceHandlerOp...
  function getFileExt (line 135) | func getFileExt(name string) string {

FILE: pkg/tree_node.go
  constant nodeTypeRoot (line 10) | nodeTypeRoot = iota
  constant nodeTypeAny (line 13) | nodeTypeAny
  constant nodeTypeParam (line 16) | nodeTypeParam
  constant nodeTypeReg (line 19) | nodeTypeReg
  constant nodeTypeStatic (line 22) | nodeTypeStatic
  constant any (line 25) | any = "*"
  type matchFunc (line 30) | type matchFunc
  type node (line 32) | type node struct
  function newStaticNode (line 47) | func newStaticNode(path string) *node {
  function newRootNode (line 59) | func newRootNode(method string) *node {
  function newNode (line 70) | func newNode(path string) *node {
  function newAnyNode (line 81) | func newAnyNode() *node {
  function newParamNode (line 94) | func newParamNode(path string) *node {

FILE: pkg/tree_router.go
  type HandlerBasedOnTree (line 13) | type HandlerBasedOnTree struct
    method ServeHTTP (line 36) | func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
    method findRouter (line 46) | func (h *HandlerBasedOnTree) findRouter(method, path string, c *Contex...
    method Route (line 72) | func (h *HandlerBasedOnTree) Route(method string, pattern string,
    method validatePattern (line 111) | func (h *HandlerBasedOnTree) validatePattern(pattern string) error {
    method findMatchChild (line 129) | func (h *HandlerBasedOnTree) findMatchChild(root *node,
    method createSubTree (line 149) | func (h *HandlerBasedOnTree) createSubTree(root *node, paths []string,...
  function NewHandlerBasedOnTree (line 24) | func NewHandlerBasedOnTree() Handler {

FILE: pkg/tree_router_test.go
  function TestHandlerBasedOnTree_Route (line 10) | func TestHandlerBasedOnTree_Route(t *testing.T) {
  function TestHandlerBasedOnTree_findRouter (line 107) | func TestHandlerBasedOnTree_findRouter(t *testing.T) {
  function handlerFuncEquals (line 153) | func handlerFuncEquals(hf1 handlerFunc, hf2 handlerFunc) bool {

FILE: pkg/v1/context.go
  type Context (line 9) | type Context struct
    method ReadJson (line 14) | func (c *Context) ReadJson(data interface{}) error {
    method OkJson (line 21) | func (c *Context) OkJson(data interface{}) error {
    method SystemErrJson (line 26) | func (c *Context) SystemErrJson(data interface{}) error {
    method BadRequestJson (line 31) | func (c *Context) BadRequestJson(data interface{}) error {
    method WriteJson (line 36) | func (c *Context) WriteJson(status int, data interface{}) error {
  function NewContext (line 49) | func NewContext(w http.ResponseWriter, r *http.Request) *Context {

FILE: pkg/v1/filter.go
  type FilterBuilder (line 8) | type FilterBuilder
  type Filter (line 10) | type Filter
  function MetricFilterBuilder (line 12) | func MetricFilterBuilder(next Filter) Filter {

FILE: pkg/v1/handler.go
  type Handler (line 3) | type Handler interface
  type handlerFunc (line 8) | type handlerFunc

FILE: pkg/v1/map_router.go
  type HandlerBasedOnMap (line 14) | type HandlerBasedOnMap struct
    method ServeHTTP (line 18) | func (h *HandlerBasedOnMap) ServeHTTP(c *Context) {
    method Route (line 31) | func (h *HandlerBasedOnMap) Route(method string, pattern string,
    method key (line 37) | func (h *HandlerBasedOnMap) key(method string,
  function NewHandlerBasedOnMap (line 42) | func NewHandlerBasedOnMap() *HandlerBasedOnMap {

FILE: pkg/v1/server.go
  type Routable (line 8) | type Routable interface
  type Server (line 14) | type Server interface
  type sdkHttpServer (line 21) | type sdkHttpServer struct
    method Route (line 28) | func (s *sdkHttpServer) Route(method string, pattern string,
    method Start (line 33) | func (s *sdkHttpServer) Start(address string) error {
  function NewSdkHttpServer (line 42) | func NewSdkHttpServer(name string, builders ...FilterBuilder) Server {

FILE: pkg/v1/tree_router.go
  type HandlerBasedOnTree (line 8) | type HandlerBasedOnTree struct
    method ServeHTTP (line 21) | func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
    method findRouter (line 60) | func (h *HandlerBasedOnTree) findRouter(path string) (handlerFunc, boo...
    method Route (line 83) | func (h *HandlerBasedOnTree) Route(method string, pattern string,
    method findMatchChild (line 109) | func (h *HandlerBasedOnTree) findMatchChild(root *node, path string) (...
    method createSubTree (line 118) | func (h *HandlerBasedOnTree) createSubTree(root *node, paths []string,...
  function NewHandlerBasedOnTree (line 13) | func NewHandlerBasedOnTree() Handler {
  type node (line 128) | type node struct
  function newNode (line 137) | func newNode(path string) *node {

FILE: pkg/v1/tree_router_test.go
  function TestHandlerBasedOnTree_Route (line 9) | func TestHandlerBasedOnTree_Route(t *testing.T) {
  function TestHandlerBasedOnTree_findRouter (line 78) | func TestHandlerBasedOnTree_findRouter(t *testing.T) {

FILE: pkg/v2/context.go
  type Context (line 9) | type Context struct
    method ReadJson (line 14) | func (c *Context) ReadJson(data interface{}) error {
    method OkJson (line 21) | func (c *Context) OkJson(data interface{}) error {
    method SystemErrJson (line 26) | func (c *Context) SystemErrJson(data interface{}) error {
    method BadRequestJson (line 31) | func (c *Context) BadRequestJson(data interface{}) error {
    method WriteJson (line 36) | func (c *Context) WriteJson(status int, data interface{}) error {
  function NewContext (line 49) | func NewContext(w http.ResponseWriter, r *http.Request) *Context {

FILE: pkg/v2/filter.go
  type FilterBuilder (line 8) | type FilterBuilder
  type Filter (line 10) | type Filter
  function MetricFilterBuilder (line 12) | func MetricFilterBuilder(next Filter) Filter {

FILE: pkg/v2/handler.go
  type Handler (line 3) | type Handler interface
  type handlerFunc (line 8) | type handlerFunc

FILE: pkg/v2/map_router.go
  type HandlerBasedOnMap (line 14) | type HandlerBasedOnMap struct
    method ServeHTTP (line 18) | func (h *HandlerBasedOnMap) ServeHTTP(c *Context) {
    method Route (line 31) | func (h *HandlerBasedOnMap) Route(method string, pattern string,
    method key (line 38) | func (h *HandlerBasedOnMap) key(method string,
  function NewHandlerBasedOnMap (line 43) | func NewHandlerBasedOnMap() *HandlerBasedOnMap {

FILE: pkg/v2/server.go
  type Routable (line 8) | type Routable interface
  type Server (line 14) | type Server interface
  type sdkHttpServer (line 21) | type sdkHttpServer struct
    method Route (line 28) | func (s *sdkHttpServer) Route(method string, pattern string,
    method Start (line 33) | func (s *sdkHttpServer) Start(address string) error {
    method ServeHTTP (line 37) | func (s *sdkHttpServer) ServeHTTP(writer http.ResponseWriter, request ...
  function NewSdkHttpServer (line 42) | func NewSdkHttpServer(name string, builders ...FilterBuilder) Server {

FILE: pkg/v2/tree_router.go
  type HandlerBasedOnTree (line 11) | type HandlerBasedOnTree struct
    method ServeHTTP (line 27) | func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
    method findRouter (line 66) | func (h *HandlerBasedOnTree) findRouter(path string) (handlerFunc, boo...
    method Route (line 89) | func (h *HandlerBasedOnTree) Route(method string, pattern string,
    method validatePattern (line 123) | func (h *HandlerBasedOnTree) validatePattern(pattern string) error {
    method findMatchChild (line 141) | func (h *HandlerBasedOnTree) findMatchChild(root *node, path string) (...
    method createSubTree (line 158) | func (h *HandlerBasedOnTree) createSubTree(root *node, paths []string,...
  function NewHandlerBasedOnTree (line 17) | func NewHandlerBasedOnTree() Handler {
  type node (line 168) | type node struct
  function newNode (line 177) | func newNode(path string) *node {

FILE: pkg/v2/tree_router_test.go
  function TestHandlerBasedOnTree_Route (line 10) | func TestHandlerBasedOnTree_Route(t *testing.T) {
  function TestHandlerBasedOnTree_findRouter (line 93) | func TestHandlerBasedOnTree_findRouter(t *testing.T) {
  function handlerFuncEquals (line 132) | func handlerFuncEquals(hf1 handlerFunc, hf2 handlerFunc) bool {

FILE: pkg/v3/context.go
  type Context (line 9) | type Context struct
    method ReadJson (line 15) | func (c *Context) ReadJson(data interface{}) error {
    method OkJson (line 22) | func (c *Context) OkJson(data interface{}) error {
    method SystemErrJson (line 27) | func (c *Context) SystemErrJson(data interface{}) error {
    method BadRequestJson (line 32) | func (c *Context) BadRequestJson(data interface{}) error {
    method WriteJson (line 37) | func (c *Context) WriteJson(status int, data interface{}) error {
  function NewContext (line 50) | func NewContext(w http.ResponseWriter, r *http.Request) *Context {

FILE: pkg/v3/filter.go
  type FilterBuilder (line 8) | type FilterBuilder
  type Filter (line 10) | type Filter
  function MetricFilterBuilder (line 12) | func MetricFilterBuilder(next Filter) Filter {

FILE: pkg/v3/handler.go
  type Handler (line 3) | type Handler interface
  type handlerFunc (line 8) | type handlerFunc

FILE: pkg/v3/map_router.go
  type HandlerBasedOnMap (line 14) | type HandlerBasedOnMap struct
    method ServeHTTP (line 18) | func (h *HandlerBasedOnMap) ServeHTTP(c *Context) {
    method Route (line 31) | func (h *HandlerBasedOnMap) Route(method string, pattern string,
    method key (line 38) | func (h *HandlerBasedOnMap) key(method string,
  function NewHandlerBasedOnMap (line 43) | func NewHandlerBasedOnMap() *HandlerBasedOnMap {

FILE: pkg/v3/server.go
  type Routable (line 8) | type Routable interface
  type Server (line 14) | type Server interface
  type sdkHttpServer (line 21) | type sdkHttpServer struct
    method Route (line 28) | func (s *sdkHttpServer) Route(method string, pattern string,
    method Start (line 33) | func (s *sdkHttpServer) Start(address string) error {
    method ServeHTTP (line 37) | func (s *sdkHttpServer) ServeHTTP(writer http.ResponseWriter, request ...
  function NewSdkHttpServer (line 42) | func NewSdkHttpServer(name string, builders ...FilterBuilder) Server {

FILE: pkg/v3/tree_node.go
  constant nodeTypeRoot (line 10) | nodeTypeRoot = iota
  constant nodeTypeAny (line 13) | nodeTypeAny
  constant nodeTypeParam (line 16) | nodeTypeParam
  constant nodeTypeReg (line 19) | nodeTypeReg
  constant nodeTypeStatic (line 22) | nodeTypeStatic
  constant any (line 25) | any = "*"
  type matchFunc (line 30) | type matchFunc
  type node (line 32) | type node struct
  function newStaticNode (line 47) | func newStaticNode(path string) *node {
  function newRootNode (line 59) | func newRootNode(method string) *node {
  function newNode (line 70) | func newNode(path string) *node {
  function newAnyNode (line 81) | func newAnyNode() *node {
  function newParamNode (line 94) | func newParamNode(path string) *node {

FILE: pkg/v3/tree_router.go
  type HandlerBasedOnTree (line 13) | type HandlerBasedOnTree struct
    method ServeHTTP (line 36) | func (h *HandlerBasedOnTree) ServeHTTP(c *Context) {
    method findRouter (line 46) | func (h *HandlerBasedOnTree) findRouter(method, path string, c *Contex...
    method Route (line 72) | func (h *HandlerBasedOnTree) Route(method string, pattern string,
    method validatePattern (line 111) | func (h *HandlerBasedOnTree) validatePattern(pattern string) error {
    method findMatchChild (line 129) | func (h *HandlerBasedOnTree) findMatchChild(root *node,
    method createSubTree (line 149) | func (h *HandlerBasedOnTree) createSubTree(root *node, paths []string,...
  function NewHandlerBasedOnTree (line 24) | func NewHandlerBasedOnTree() Handler {

FILE: pkg/v3/tree_router_test.go
  function TestHandlerBasedOnTree_Route (line 10) | func TestHandlerBasedOnTree_Route(t *testing.T) {
  function TestHandlerBasedOnTree_findRouter (line 107) | func TestHandlerBasedOnTree_findRouter(t *testing.T) {
  function handlerFuncEquals (line 153) | func handlerFuncEquals(hf1 handlerFunc, hf2 handlerFunc) bool {
Condensed preview — 96 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (117K chars).
[
  {
    "path": ".gitignore",
    "chars": 276,
    "preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Ou"
  },
  {
    "path": "LICENSE",
    "chars": 1066,
    "preview": "MIT License\n\nCopyright (c) 2021 Ming Deng\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
  },
  {
    "path": "README.md",
    "chars": 23,
    "preview": "# toy-web\n用于极客时间go基础课程\n"
  },
  {
    "path": "demo/filters/filter.go",
    "chars": 262,
    "preview": "package filters\n\nimport (\n\t\"fmt\"\n\tweb \"geektime/toy-web/pkg\"\n)\n\nfunc init() {\n\tweb.RegisterFilter(\"my-custom\", myFilterB"
  },
  {
    "path": "demo/user.go",
    "chars": 813,
    "preview": "package demo\n\nimport (\n\t\"fmt\"\n\tweb \"geektime/toy-web/pkg\"\n\t\"time\"\n)\n\nfunc SignUp(c *web.Context) {\n\treq := &signUpReq{}\n"
  },
  {
    "path": "examples/first_lesson/afterclass/fibonacci.go",
    "chars": 81,
    "preview": "package main\n\nfunc main() {\n\n}\n\nfunc fibonacci(n int) int {\n\t// TODO\n\treturn 0\n}\n"
  },
  {
    "path": "examples/first_lesson/afterclass/fmt.go",
    "chars": 154,
    "preview": "package main\n\nfunc main() {\n\t\n}\n\n// 输出两位小数\nfunc printNumWith2(float642 float64) string {\n\treturn \"\"\n}\n\nfunc printBytes(d"
  },
  {
    "path": "examples/first_lesson/afterclass/slice.go",
    "chars": 451,
    "preview": "package main\n\nfunc main() {\n\ts := []int{1, 2, 4, 7}\n\t// 结果应该是 5, 1, 2, 4, 7\n\ts = Add(s, 0, 5)\n\n\t// 结果应该是5, 9, 1, 2, 4, 7"
  },
  {
    "path": "examples/first_lesson/array_slice/array.go",
    "chars": 411,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\t// 直接初始化一个三个元素的数组。大括号里面多一个或者少一个都编译不通过\n\ta1 := [3]int{9, 8, 7}\n\tfmt.Printf(\"a1:"
  },
  {
    "path": "examples/first_lesson/array_slice/slice.go",
    "chars": 1773,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ts1 := []int{1, 2, 3, 4} // 直接初始化了 4 个元素的切片\n\tfmt.Printf(\"s1: %v, len %d, cap: "
  },
  {
    "path": "examples/first_lesson/fmt/fmt.go",
    "chars": 541,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tname:=\"Tom\"\n\tage := 17\n\t// 这个 API 是返回字符串的,所以大多数时候我们都是用这个\n\tstr := fmt.Sprintf("
  },
  {
    "path": "examples/first_lesson/for/for.go",
    "chars": 828,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tForLoop()\n\tForI()\n\tForR()\n}\n\nfunc ForLoop()  {\n\tarr := []int {9, 8, 7, 6}\n\tin"
  },
  {
    "path": "examples/first_lesson/func_dec/funcs.go",
    "chars": 976,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := Fun0(\"Tom\")\n\tprintln(a)\n\n\tb, c := Fun1(\"a\", 17)\n\tprintln(b)\n\tprintln(c)\n"
  },
  {
    "path": "examples/first_lesson/if_else/ifelse.go",
    "chars": 524,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tYoung(9)\n\tYoung(100)\n\n\tIfUsingNewVariable(10, 200)\n\tIfUsingNewVariable(100, 3"
  },
  {
    "path": "examples/first_lesson/package_dec/multi_same/a.go",
    "chars": 19,
    "preview": "package multi_same\n"
  },
  {
    "path": "examples/first_lesson/package_dec/multi_same/b.go",
    "chars": 19,
    "preview": "package multi_same\n"
  },
  {
    "path": "examples/first_lesson/package_dec/not_same/not_same.go",
    "chars": 22,
    "preview": "package not_same_aaaa\n"
  },
  {
    "path": "examples/first_lesson/switch/switch.go",
    "chars": 277,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tChooseFruit(\"蓝莓\")\n\tChooseFruit(\"苹果\")\n\tChooseFruit(\"西瓜\")\n}\n\nfunc ChooseFruit(f"
  },
  {
    "path": "examples/first_lesson/types/rune.go",
    "chars": 47,
    "preview": "package main\n\nfunc main() {\n\tvar a byte = 13\n}\n"
  },
  {
    "path": "examples/first_lesson/types/string.go",
    "chars": 430,
    "preview": "package main\n\nimport \"unicode/utf8\"\n\nfunc main() {\n\t// 一般推荐用于短的,不用换行的,不含双引号的\n\tprintln(\"He said:\\\" Hello Go \\\" \")\n\t// 长的,"
  },
  {
    "path": "examples/first_lesson/var_and_const/assignment.go",
    "chars": 79,
    "preview": "package main\n\nfunc main() {\n\t a := 13\n\t println(a)\n\t b := \"你好\"\n\t println(b)\n}\n\n"
  },
  {
    "path": "examples/first_lesson/var_and_const/const.go",
    "chars": 109,
    "preview": "package main\n\nconst internal = \"包内可访问\"\nconst External = \"包外可访问\"\n\nfunc main() {\n\tconst a = \"你好\"\n\tprintln(a)\n}\n"
  },
  {
    "path": "examples/first_lesson/var_and_const/var.go",
    "chars": 486,
    "preview": "package main\n\n// Global 首字母大写,全局可以访问\nvar Global = \"全局变量\"\n\n// 首字母小写,只能在这个包里面使用\n// 其子包也不能用\nvar local = \"包变量\"\n\nvar (\n\tFirst"
  },
  {
    "path": "examples/first_lesson/var_and_const/var_wrong.go",
    "chars": 287,
    "preview": "package main\n\nvar aa = \"hello\"\n// var aa = \"bbb\" 这个包已经有一个 a 了,所以再次声明会导致编译\nfunc main() {\n\taa := 13 // 虽然包外面已经有一个 aa 了,但是这"
  },
  {
    "path": "examples/forth_lesson/atomic/atomic.go",
    "chars": 540,
    "preview": "package main\n\nimport \"sync/atomic\"\n\nvar value int32 = 0\nfunc main() {\n\t// 要传入 value 的指针\n\t// 把 value + 10\n\tatomic.AddInt3"
  },
  {
    "path": "examples/forth_lesson/channel/channel.go",
    "chars": 712,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tchannelWithoutCache()\n\tchannelWithCache()\n}\n\nfunc channelWithCac"
  },
  {
    "path": "examples/forth_lesson/context/context.go",
    "chars": 1329,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tWithTimeout()\n\tWithCancel()\n\tWithDeadline()\n\tWithValu"
  },
  {
    "path": "examples/forth_lesson/init/init_order.go",
    "chars": 227,
    "preview": "package main\n\nfunc init() {\n\t// 因为我们不能确定 init 方法的执行顺序,\n\t// 只能曲线救国\n\tinitBeforeSomething()\n\tinitSomething()\n\tinitAfterSome"
  },
  {
    "path": "examples/forth_lesson/init/multi_init.go",
    "chars": 63,
    "preview": "package main\n\nfunc init() {\n\t// 第一个\n}\n\nfunc init() {\n\t// 第二个\n}\n"
  },
  {
    "path": "examples/forth_lesson/select/select.go",
    "chars": 461,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\t// 这个不能在 main 函数运行,是因为运行起来,\n\t// 所有的goroutine都被我们搞sleep了,直接就崩了\n\t/"
  },
  {
    "path": "examples/forth_lesson/static_resource/file_server.go",
    "chars": 154,
    "preview": "package main\n\nimport \"net/http\"\n\nfunc main() {\n\tserve := http.FileServer(http.Dir(\".\"))\n\t//http.Handle(\"/\", serve)\n\thttp"
  },
  {
    "path": "examples/second_lesson/afterclass/set.go",
    "chars": 242,
    "preview": "package afterclass\n\ntype Set interface {\n\tPut(key string)\n\tKeys() []string\n\tContains(key string) bool\n\tRemove(key string"
  },
  {
    "path": "examples/second_lesson/afterclass/tree.go",
    "chars": 119,
    "preview": "package afterclass\n\ntype Tree interface {\n\n}\n\n// 二叉树\ntype binaryTree struct {\n\n}\n\n// 多叉树\ntype mutliWayTree struct {\n\n}\n"
  },
  {
    "path": "examples/second_lesson/composition/composition.go",
    "chars": 565,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\n}\n\n// Swimming 会游泳的\ntype Swimming interface {\n\tSwim()\n}\n\ntype Duck interface "
  },
  {
    "path": "examples/second_lesson/composition/no_over_write.go",
    "chars": 327,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tson := Son{\n\t\tParent{},\n\t}\n\n\tson.SayHello()\n}\n\ntype Parent struct {\n\n}\n\nfunc "
  },
  {
    "path": "examples/second_lesson/http/request_body.go",
    "chars": 1850,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc home(w http.ResponseWriter, r *http.Request)  {"
  },
  {
    "path": "examples/second_lesson/map/map.go",
    "chars": 498,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\t// 创建了一个预估容量是2的 map\n\tm := make(map[string]string, 2)\n\t// 没有指定预估容量\n\tm1 := make"
  },
  {
    "path": "examples/second_lesson/server_context/signup.go",
    "chars": 1237,
    "preview": "package server_context\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"geektime/toy-web/pkg/v2\"\n\t\"io\"\n\t\"net/http\"\n)\n\n// 在没有 context "
  },
  {
    "path": "examples/second_lesson/struct/intf.go",
    "chars": 193,
    "preview": "package main\n\n// 首字母小写,所以是一个包私有的接口\ntype animal interface {\n\t// 这里可以有任意多个方法,不过我们一般建议是小接口,\n\t// 即接口里面不会有很多方法\n\t// 方法声明不需要 fu"
  },
  {
    "path": "examples/second_lesson/struct/pointer.go",
    "chars": 231,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\t// 指针用 * 表示\n\tvar p *ToyDuck = &ToyDuck{}\n\t// 解引用,得到结构体\n\tvar duck ToyDuck = *p"
  },
  {
    "path": "examples/second_lesson/struct/receiver.go",
    "chars": 575,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\n\t// 因为 u 是结构体,所以方法调用的时候它数据是不会变的\n\tu := User{\n\t\tName: \"Tom\",\n\t\tAge: 10,\n\t}\n\tu.C"
  },
  {
    "path": "examples/second_lesson/struct/self_ref.go",
    "chars": 184,
    "preview": "package main\n\nfunc main() {\n\n}\n\ntype Node struct {\n\t//自引用只能使用指针\n\t//left Node\n\t//right Node\n\n\tleft *Node\n\tright *Node\n\n\t/"
  },
  {
    "path": "examples/second_lesson/struct/struct.go",
    "chars": 711,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\t// duck1 是 *ToyDuck\n\tduck1 := &ToyDuck{}\n\tduck1.Swim()\n\n\tduck2 := ToyDuck{}\n\t"
  },
  {
    "path": "examples/second_lesson/struct/type_a_b.go",
    "chars": 570,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfake := FakeFish{}\n\t// fake 无法调用原来 Fish 的方法\n\t// 这一句会编译错误\n\t//fake.Swim()\n\tfake"
  },
  {
    "path": "examples/second_lesson/struct/type_a_et_b.go",
    "chars": 221,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar n News = fakeNews{\n\t\tName: \"hello\",\n\t}\n\tn.Report()\n}\n\ntype News struct {\n"
  },
  {
    "path": "examples/third_lesson/closure/closure.go",
    "chars": 489,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\n\ti := 13\n\ta := func() {\n\t\tfmt.Printf(\"i is %d \\n\", i)\n\t}\n\ta()\n\n\t"
  },
  {
    "path": "examples/third_lesson/defer/defer.go",
    "chars": 172,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tdefer func() {\n\t\tfmt.Println(\"aaa\")\n\t}()\n\n\tdefer func() {\n\t\tfmt.Println(\"bbb\""
  },
  {
    "path": "examples/third_lesson/errors/error.go",
    "chars": 715,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar err error = &MyError{}\n\tprintln(err.Error())\n\n\tErrorsPkg()"
  },
  {
    "path": "examples/third_lesson/errors/panic.go",
    "chars": 220,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tdefer func() {\n\t\tif data := recover(); data != nil {\n\t\t\tfmt.Printf(\"hello, pa"
  },
  {
    "path": "examples/third_lesson/goroutine/goroutine.go",
    "chars": 184,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tGoRoutine()\n}\n\nfunc GoRoutine() {\n\tgo func() {\n\t\ttime.Sleep(10 *"
  },
  {
    "path": "examples/third_lesson/sync/map.go",
    "chars": 212,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\nfunc main() {\n\tm := sync.Map{}\n\tm.Store(\"cat\", \"Tom\")\n\tm.Store(\"mouse\", \"Jerry\""
  },
  {
    "path": "examples/third_lesson/sync/mutex.go",
    "chars": 571,
    "preview": "package main\n\nimport (\n\t\"sync\"\n)\n\nvar mutex sync.Mutex\nvar rwMutex sync.RWMutex\nfunc Mutex() {\n\tmutex.Lock()\n\tdefer mute"
  },
  {
    "path": "examples/third_lesson/sync/once.go",
    "chars": 205,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\nfunc main() {\n\tPrintOnce()\n\tPrintOnce()\n\tPrintOnce()\n}\n\nvar once sync.Once\n\n// "
  },
  {
    "path": "examples/third_lesson/sync/pool.go",
    "chars": 453,
    "preview": "package main\n\nimport \"sync\"\n\nfunc main() {\n\tpool := sync.Pool{\n\t\tNew: func() interface{}{\n\t\t\treturn &user{}\n\t}}\n\n\t// Get"
  },
  {
    "path": "examples/third_lesson/sync/wait_group.go",
    "chars": 242,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\nfunc main() {\n\tres := 0\n\twg := sync.WaitGroup{}\n\twg.Add(10)\n\tfor i := 0; i < 10"
  },
  {
    "path": "go.mod",
    "chars": 146,
    "preview": "module geektime/toy-web\n\ngo 1.16\n\nrequire (\n\tgithub.com/hashicorp/golang-lru v0.5.4 // indirect\n\tgithub.com/stretchr/tes"
  },
  {
    "path": "go.sum",
    "chars": 1184,
    "preview": "github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=\ngithub.com/davecgh/go-spew v1.1.0/go.m"
  },
  {
    "path": "main.go",
    "chars": 2229,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"geektime/toy-web/demo\"\n\t_ \"geektime/toy-web/demo/filters\"\n\t\"geektime/toy-web/"
  },
  {
    "path": "onclass/main.go",
    "chars": 715,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc home(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprintf(w, \"这是主页\")"
  },
  {
    "path": "pkg/context.go",
    "chars": 1335,
    "preview": "package web\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\ntype Context struct {\n\tW http.ResponseWriter\n\tR *http"
  },
  {
    "path": "pkg/filter.go",
    "chars": 667,
    "preview": "package web\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype FilterBuilder func(next Filter) Filter\n\ntype Filter func(c *Context)\n\nfunc "
  },
  {
    "path": "pkg/graceful_shutdown.go",
    "chars": 2354,
    "preview": "package web\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/signal\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nvar ErrorHook"
  },
  {
    "path": "pkg/graceful_shutdown_signal_darwin.go",
    "chars": 1140,
    "preview": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOT"
  },
  {
    "path": "pkg/graceful_shutdown_signal_linux.go",
    "chars": 1336,
    "preview": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOT"
  },
  {
    "path": "pkg/graceful_shutdown_signal_windows.go",
    "chars": 1106,
    "preview": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOT"
  },
  {
    "path": "pkg/handler.go",
    "chars": 107,
    "preview": "package web\n\ntype Handler interface {\n\tServeHTTP(c *Context)\n\tRoutable\n}\n\ntype handlerFunc func(c *Context)"
  },
  {
    "path": "pkg/hook.go",
    "chars": 888,
    "preview": "package web\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Hook 是一个钩子函数。注意,\n// ctx 是一个有超时机制的 context.Context\n// 所以你必须"
  },
  {
    "path": "pkg/hook_test.go",
    "chars": 510,
    "preview": "package web\n\nimport (\n\t\"context\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestBuildCloseServerHo"
  },
  {
    "path": "pkg/map_router.go",
    "chars": 867,
    "preview": "package web\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n)\n\n// 一种常用的GO设计模式,\n// 用于确保HandlerBasedOnMap肯定实现了这个接口\nvar _ Handler = &H"
  },
  {
    "path": "pkg/server.go",
    "chars": 2034,
    "preview": "package web\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Routable 可路由的\ntype Routable interface {\n\t// Ro"
  },
  {
    "path": "pkg/static_resource.go",
    "chars": 3224,
    "preview": "package web\n\nimport (\n\t\"fmt\"\n\tlru \"github.com/hashicorp/golang-lru\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"st"
  },
  {
    "path": "pkg/tree_node.go",
    "chars": 1986,
    "preview": "package web\n\nimport (\n\t\"strings\"\n)\n\nconst (\n\n\t// 根节点,只有根用这个\n\tnodeTypeRoot = iota\n\n\t// *\n\tnodeTypeAny\n\n\t// 路径参数\n\tnodeType"
  },
  {
    "path": "pkg/tree_router.go",
    "chars": 3372,
    "preview": "package web\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strings\"\n)\n\nvar ErrorInvalidRouterPattern = errors.New(\"invalid ro"
  },
  {
    "path": "pkg/tree_router_test.go",
    "chars": 5086,
    "preview": "package web\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestHandlerBasedOn"
  },
  {
    "path": "pkg/v1/context.go",
    "chars": 1014,
    "preview": "package webv1\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n)\n\ntype Context struct {\n\tW http.ResponseWriter\n\tR *http.Requ"
  },
  {
    "path": "pkg/v1/filter.go",
    "chars": 352,
    "preview": "package webv1\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype FilterBuilder func(next Filter) Filter\n\ntype Filter func(c *Context)\n\nfun"
  },
  {
    "path": "pkg/v1/handler.go",
    "chars": 109,
    "preview": "package webv1\n\ntype Handler interface {\n\tServeHTTP(c *Context)\n\tRoutable\n}\n\ntype handlerFunc func(c *Context)"
  },
  {
    "path": "pkg/v1/map_router.go",
    "chars": 851,
    "preview": "package webv1\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n)\n\n// 一种常用的GO设计模式,\n// 用于确保HandlerBasedOnMap肯定实现了这个接口\nvar _ Handler = "
  },
  {
    "path": "pkg/v1/server.go",
    "chars": 1253,
    "preview": "package webv1\n\nimport (\n\t\"net/http\"\n)\n\n// Routable 可路由的\ntype Routable interface {\n\t// Route 设定一个路由,命中该路由的会执行handlerFunc的"
  },
  {
    "path": "pkg/v1/tree_router.go",
    "chars": 2934,
    "preview": "package webv1\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n)\n\ntype HandlerBasedOnTree struct {\n\troot *node\n\n}\n\nfunc NewHandlerBasedO"
  },
  {
    "path": "pkg/v1/tree_router_test.go",
    "chars": 3069,
    "preview": "package webv1\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/http\"\n\t\"testing\"\n)\n\nfunc TestHandlerBasedOnTree_Rout"
  },
  {
    "path": "pkg/v2/context.go",
    "chars": 1014,
    "preview": "package webv2\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n)\n\ntype Context struct {\n\tW http.ResponseWriter\n\tR *http.Requ"
  },
  {
    "path": "pkg/v2/filter.go",
    "chars": 352,
    "preview": "package webv2\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype FilterBuilder func(next Filter) Filter\n\ntype Filter func(c *Context)\n\nfun"
  },
  {
    "path": "pkg/v2/handler.go",
    "chars": 109,
    "preview": "package webv2\n\ntype Handler interface {\n\tServeHTTP(c *Context)\n\tRoutable\n}\n\ntype handlerFunc func(c *Context)"
  },
  {
    "path": "pkg/v2/map_router.go",
    "chars": 869,
    "preview": "package webv2\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n)\n\n// 一种常用的GO设计模式,\n// 用于确保HandlerBasedOnMap肯定实现了这个接口\nvar _ Handler = "
  },
  {
    "path": "pkg/v2/server.go",
    "chars": 1279,
    "preview": "package webv2\n\nimport (\n\t\"net/http\"\n)\n\n// Routable 可路由的\ntype Routable interface {\n\t// Route 设定一个路由,命中该路由的会执行handlerFunc的"
  },
  {
    "path": "pkg/v2/tree_router.go",
    "chars": 3780,
    "preview": "package webv2\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nvar ErrorInvalidRouterPattern = errors.New(\"invalid router p"
  },
  {
    "path": "pkg/v2/tree_router_test.go",
    "chars": 4205,
    "preview": "package webv2\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestHandlerBased"
  },
  {
    "path": "pkg/v3/context.go",
    "chars": 1112,
    "preview": "package webv3\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n)\n\ntype Context struct {\n\tW http.ResponseWriter\n\tR *http.Requ"
  },
  {
    "path": "pkg/v3/filter.go",
    "chars": 352,
    "preview": "package webv3\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype FilterBuilder func(next Filter) Filter\n\ntype Filter func(c *Context)\n\nfun"
  },
  {
    "path": "pkg/v3/handler.go",
    "chars": 109,
    "preview": "package webv3\n\ntype Handler interface {\n\tServeHTTP(c *Context)\n\tRoutable\n}\n\ntype handlerFunc func(c *Context)"
  },
  {
    "path": "pkg/v3/map_router.go",
    "chars": 869,
    "preview": "package webv3\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n)\n\n// 一种常用的GO设计模式,\n// 用于确保HandlerBasedOnMap肯定实现了这个接口\nvar _ Handler = "
  },
  {
    "path": "pkg/v3/server.go",
    "chars": 1279,
    "preview": "package webv3\n\nimport (\n\t\"net/http\"\n)\n\n// Routable 可路由的\ntype Routable interface {\n\t// Route 设定一个路由,命中该路由的会执行handlerFunc的"
  },
  {
    "path": "pkg/v3/tree_node.go",
    "chars": 1988,
    "preview": "package webv3\n\nimport (\n\t\"strings\"\n)\n\nconst (\n\n\t// 根节点,只有根用这个\n\tnodeTypeRoot = iota\n\n\t// *\n\tnodeTypeAny\n\n\t// 路径参数\n\tnodeTy"
  },
  {
    "path": "pkg/v3/tree_router.go",
    "chars": 3374,
    "preview": "package webv3\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strings\"\n)\n\nvar ErrorInvalidRouterPattern = errors.New(\"invalid "
  },
  {
    "path": "pkg/v3/tree_router_test.go",
    "chars": 5088,
    "preview": "package webv3\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestHandlerBased"
  }
]

About this extraction

This page contains the full source code of the flycash/toy-web GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 96 files (88.0 KB), approximately 31.6k tokens, and a symbol index with 342 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!