├── .gitignore ├── Chapter02 ├── branch │ ├── abc.txt │ └── branch.go ├── loop │ └── loop.go ├── basic │ └── basic.go └── func │ └── func.go ├── Chapter12 └── maze │ ├── maze.in │ └── maze.go ├── Chapter07 ├── errhandling │ ├── fib │ │ └── fib.go │ ├── main.go │ └── defer │ │ └── defer.go ├── recover │ └── recover.go └── filelistingserver │ ├── filelisting │ └── handler.go │ └── web.go ├── Chapter08 ├── basic │ ├── basic.go │ └── triangle_test.go ├── queue │ ├── queue_test.go │ └── queue.go ├── nonrepeatingsubstr │ ├── nonrepeating.go │ └── nonrepeating_test.go └── filelistingserver │ ├── filelisting │ └── handler.go │ ├── web.go │ └── errwrapper_test.go ├── Chapter04 ├── queue │ ├── entry │ │ └── main.go │ └── queue.go ├── tree │ ├── node.go │ ├── traverse.go │ └── entry │ │ └── entry.go └── node.go ├── README.md ├── Chapter09 └── goroutine │ └── goroutine.go ├── Chapter05 └── retriever │ ├── mock │ └── mockretriever.go │ ├── real │ └── retriever.go │ └── main.go ├── Chapter10 ├── tree │ ├── node.go │ ├── traverse.go │ └── entry │ │ └── entry.go ├── atomic │ └── atomic.go ├── done │ └── done.go ├── channel │ └── channel.go └── select │ └── select.go ├── Chapter06 └── functional │ ├── adder │ └── adder.go │ └── fib.go ├── Chapter03 ├── strings.go ├── maps.go ├── sliceops.go ├── nonrepeating.go ├── slices.go └── arrays.go ├── Chapter11 ├── filelistingserver │ ├── filelisting │ │ └── handler.go │ ├── web.go │ └── errwrapper_test.go └── http │ └── client.go └── Chapter13 └── http ├── client.go └── gindemo └── ginserver.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | -------------------------------------------------------------------------------- /Chapter02/branch/abc.txt: -------------------------------------------------------------------------------- 1 | abcde 2 | 12345 3 | hello 4 | again -------------------------------------------------------------------------------- /Chapter12/maze/maze.in: -------------------------------------------------------------------------------- 1 | 6 5 2 | 0 1 0 0 0 3 | 0 0 0 1 0 4 | 0 1 0 1 0 5 | 1 1 1 0 0 6 | 0 1 0 0 1 7 | 0 1 0 0 0 -------------------------------------------------------------------------------- /Chapter07/errhandling/fib/fib.go: -------------------------------------------------------------------------------- 1 | package fib 2 | 3 | func Fibonacci() func() int { 4 | a, b := 0, 1 5 | return func() int { 6 | a, b = b, a+b 7 | return a 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter08/basic/basic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func triangle() { 9 | var a, b int = 3, 4 10 | fmt.Println(calcTriangle(a, b)) 11 | } 12 | 13 | func calcTriangle(a, b int) int { 14 | var c int 15 | c = int(math.Sqrt(float64((a*a + b*b)))) 16 | return c 17 | } 18 | -------------------------------------------------------------------------------- /Chapter08/queue/queue_test.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import "fmt" 4 | 5 | func ExampleQueue_Pop() { 6 | q := Queue{1} 7 | q.Push(2) 8 | q.Push(3) 9 | fmt.Println(q.Pop()) 10 | fmt.Println(q.Pop()) 11 | fmt.Println(q.IsEmpty()) 12 | 13 | fmt.Println(q.Pop()) 14 | fmt.Println(q.IsEmpty()) 15 | 16 | // Output: 17 | // 1 18 | // 2 19 | // false 20 | // 3 21 | // true 22 | } -------------------------------------------------------------------------------- /Chapter04/queue/entry/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "learninggo/Chapter04/queue" 6 | ) 7 | 8 | func main() { 9 | q := queue.Queue{1} 10 | 11 | q.Push(2) 12 | q.Push(3) 13 | fmt.Println(q.Pop()) 14 | fmt.Println(q.Pop()) 15 | fmt.Println(q.IsEmpty()) 16 | fmt.Println(q.Pop()) 17 | fmt.Println(q.IsEmpty()) 18 | 19 | //q.Push("abc") 20 | //fmt.Println(q.Pop()) 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google资深工程师深度讲解Go语言 2 | 3 | 基本语法、函数式编程、面向接口、并发编程、分布式爬虫实战 全面掌握Go语言 4 | 5 | ## 主要内容 6 | 7 | 学习笔记链接:https://alanhou.org/learning-go/ 8 | 9 | * Go语言的安装与开发环境 10 | * 基础语法 11 | * 内建容器 12 | * 面向“对象” 13 | * 面向接口 14 | * 函数式编程 15 | * 错误处理和资源管理 16 | * 测试与性能调优 17 | * Goroutine 18 | * Channel 19 | * http及其他标准库 20 | * 迷宫的广度优先搜索 21 | * 开始实战项目 22 | * 单任务版爬虫 23 | * 并发版爬虫 24 | * 数据存储和展示 25 | * 分布式爬虫 26 | * 课程总结 27 | 28 | Credit:慕课网 -------------------------------------------------------------------------------- /Chapter04/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | //type Queue []int 4 | type Queue []interface{} 5 | 6 | func (q *Queue) Push(v int) { 7 | //func (q *Queue) Push(v interface{}) { 8 | *q = append(*q, v) 9 | } 10 | 11 | func (q *Queue) Pop() int { 12 | //func (q *Queue) Pop() interface{} { 13 | head := (*q)[0] 14 | *q = (*q)[1:] 15 | return head.(int) 16 | } 17 | 18 | func (q *Queue) IsEmpty() bool { 19 | return len(*q) == 0 20 | } 21 | -------------------------------------------------------------------------------- /Chapter09/goroutine/goroutine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | //var a [10]int 10 | for i := 0; i < 10; i++ { 11 | go func(i int) { // race condition 12 | for { 13 | fmt.Printf("Hello from goroutine %d\n", i) 14 | //a[i]++ 15 | //runtime.Gosched() 16 | } 17 | }(i) 18 | } 19 | time.Sleep(time.Minute) 20 | //time.Sleep(time.Millisecond) 21 | //fmt.Println(a) 22 | } 23 | -------------------------------------------------------------------------------- /Chapter04/tree/node.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import "fmt" 4 | 5 | type Node struct { 6 | Value int 7 | Left, Right *Node 8 | } 9 | 10 | func (node Node) Print(){ 11 | fmt.Print(node.Value, " ") 12 | } 13 | 14 | func (node *Node) SetValue(value int) { 15 | if node == nil { 16 | fmt.Println("Setting value to nil node. Ignored!") 17 | return 18 | } 19 | node.Value = value 20 | } 21 | 22 | 23 | func CreateNode(value int) *Node { 24 | return &Node{Value: value} 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter05/retriever/mock/mockretriever.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import "fmt" 4 | 5 | type Retriever struct { 6 | Contents string 7 | } 8 | 9 | func (r *Retriever) String() string { 10 | return fmt.Sprintf( 11 | "Retriever: {Contents=%s}", r.Contents) 12 | } 13 | 14 | func (r *Retriever) Post(url string, 15 | form map[string]string) string { 16 | r.Contents = form["contents"] 17 | return "ok" 18 | } 19 | 20 | func (r *Retriever) Get(url string) string { 21 | return r.Contents 22 | } 23 | -------------------------------------------------------------------------------- /Chapter08/basic/triangle_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestTriangle(t *testing.T) { 6 | tests := []struct {a, b, c int} { 7 | {3, 4, 5}, 8 | {5, 12, 13}, 9 | {8, 15, 17}, 10 | {12, 35, 37}, 11 | {30000, 40000, 50000}, 12 | } 13 | 14 | for _, tt := range tests { 15 | if actual := calcTriangle(tt.a, tt.b); actual != tt.c{ 16 | t.Errorf("calcTriangle(%d, %d); " + 17 | "got %d; expected %d", 18 | tt.a, tt.b, actual, tt.c) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Chapter10/tree/node.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import "fmt" 4 | 5 | type Node struct { 6 | Value int 7 | Left, Right *Node 8 | } 9 | 10 | func (node Node) Print(){ 11 | fmt.Print(node.Value, " ") 12 | } 13 | 14 | func (node *Node) SetValue(value int) { 15 | if node == nil { 16 | fmt.Println("Setting value to nil node. Ignored!") 17 | return 18 | } 19 | node.Value = value 20 | } 21 | 22 | 23 | func CreateNode(value int) *Node { 24 | return &Node{Value: value} 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter07/recover/recover.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func tryRecover() { 8 | defer func() { 9 | r := recover() 10 | if err, ok := r.(error); ok { 11 | fmt.Println("Error occured:", err) 12 | } else { 13 | panic(fmt.Sprintf("I don't know what to do: %v", r)) 14 | } 15 | }() 16 | //panic(errors.New("This is an error")) 17 | 18 | //b := 0 19 | //a := 5 / b 20 | //fmt.Println(a) 21 | panic(123) 22 | } 23 | 24 | func main() { 25 | tryRecover() 26 | } 27 | -------------------------------------------------------------------------------- /Chapter08/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | // An FIFO queue. 4 | type Queue []int 5 | 6 | // Pushes the element into the queue 7 | // e.g. q.Push(123) 8 | func (q *Queue) Push(v int) { 9 | *q = append(*q, v) 10 | } 11 | 12 | // Pops element from head. 13 | func (q *Queue) Pop() int { 14 | //func (q *Queue) Pop() interface{} { 15 | head := (*q)[0] 16 | *q = (*q)[1:] 17 | return head 18 | } 19 | 20 | // Returns if the queue is empty or not 21 | func (q *Queue) IsEmpty() bool { 22 | return len(*q) == 0 23 | } 24 | -------------------------------------------------------------------------------- /Chapter05/retriever/real/retriever.go: -------------------------------------------------------------------------------- 1 | package real 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httputil" 6 | "time" 7 | ) 8 | 9 | type Retriever struct { 10 | UserAgent string 11 | TimeOut time.Duration 12 | } 13 | 14 | func (r *Retriever) Get(url string) string { 15 | resp, err := http.Get(url) 16 | if err != nil { 17 | panic(err) 18 | } 19 | 20 | result, err := httputil.DumpResponse( 21 | resp, true) 22 | 23 | resp.Body.Close() 24 | 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | return string(result) 30 | } 31 | -------------------------------------------------------------------------------- /Chapter04/tree/traverse.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import "fmt" 4 | 5 | //func (node *Node) Traverse() { 6 | // if node == nil { 7 | // return 8 | // } 9 | // 10 | // node.Left.Traverse() 11 | // node.Print() 12 | // node.Right.Traverse() 13 | //} 14 | 15 | func (node *Node) Traverse() { 16 | node.TraverseFunc(func(n *Node) { 17 | n.Print() 18 | }) 19 | fmt.Println() 20 | } 21 | 22 | func (node *Node) TraverseFunc(f func(*Node)) { 23 | if node == nil { 24 | return 25 | } 26 | 27 | node.Left.TraverseFunc(f) 28 | f(node) 29 | node.Right.TraverseFunc(f) 30 | } 31 | -------------------------------------------------------------------------------- /Chapter08/nonrepeatingsubstr/nonrepeating.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func lengthOfNonRepeatingSubStr(s string) int{ 4 | //lastOccurred := make(map [byte] int) 5 | lastOccurred := make(map [rune] int) 6 | start := 0 7 | maxLength := 0 8 | 9 | //for i, ch := range []byte(s) { 10 | for i, ch := range []rune(s) { 11 | if lastI, ok := lastOccurred[ch]; ok && lastI >= start { 12 | start = lastI + 1 13 | } 14 | if i - start + 1 > maxLength{ 15 | maxLength = i - start + 1 16 | } 17 | lastOccurred[ch] = i 18 | } 19 | 20 | return maxLength 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter10/atomic/atomic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type atomicInt struct { 10 | value int 11 | lock sync.Mutex 12 | } 13 | 14 | func (a * atomicInt) incement() { 15 | fmt.Println("safe increment") 16 | func () { 17 | a.lock.Lock() 18 | defer a.lock.Unlock() 19 | 20 | a.value++ 21 | }() 22 | } 23 | 24 | func (a *atomicInt) get() int { 25 | a.lock.Lock() 26 | defer a.lock.Unlock() 27 | 28 | return a.value 29 | } 30 | 31 | func main() { 32 | var a atomicInt 33 | a.incement() 34 | go func() { 35 | a.incement() 36 | }() 37 | time.Sleep(time.Millisecond) 38 | fmt.Println(a.get()) 39 | } 40 | -------------------------------------------------------------------------------- /Chapter06/functional/adder/adder.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func adder() func(int) int { 6 | sum := 0 7 | return func(v int) int { 8 | sum += v 9 | return sum 10 | } 11 | } 12 | 13 | // “正统”的函数式编程实现方法 14 | type iAdder func(int) (int, iAdder) 15 | 16 | func adder2(base int) iAdder { 17 | return func(v int) (int, iAdder) { 18 | return base + v, adder2(base + v) 19 | } 20 | } 21 | 22 | func main() { 23 | //a := adder() 24 | //for i := 0; i < 10; i++ { 25 | // fmt.Printf("0 + 1 + ... + %d = %d\n", 26 | // i, a(i)) 27 | //} 28 | 29 | a := adder2(0) 30 | for i := 0; i < 10; i++ { 31 | var s int 32 | s, a = a(i) 33 | fmt.Printf("0 + 1 + ... + %d = %d\n", 34 | i, s) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Chapter07/errhandling/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "learninggo/Chapter07/errhandling/fib" 8 | "strings" 9 | ) 10 | 11 | type intGen func() int 12 | 13 | func (g intGen) Read( 14 | p []byte) (n int, err error) { 15 | next := g() 16 | if next > 10000 { 17 | return 0, io.EOF 18 | } 19 | s := fmt.Sprintf("%d\n", next) 20 | 21 | // TODO: 解决 p 过小导致的问题 22 | return strings.NewReader(s).Read(p) 23 | } 24 | 25 | func printFileContents(reader io.Reader) { 26 | scanner := bufio.NewScanner(reader) 27 | 28 | for scanner.Scan() { 29 | fmt.Println(scanner.Text()) 30 | } 31 | } 32 | 33 | func main() { 34 | var f intGen = fib.Fibonacci() 35 | 36 | printFileContents(f) 37 | } 38 | -------------------------------------------------------------------------------- /Chapter03/strings.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "unicode/utf8" 6 | ) 7 | 8 | func main() { 9 | s := "Yes我爱慕课网!" // UTF-8 中文占三字节 10 | fmt.Println(s) 11 | //fmt.Println(len(s)) 12 | //fmt.Printf("%X\n", []byte(s)) 13 | for _, b := range []byte(s) { 14 | fmt.Printf("%X ", b) 15 | } 16 | fmt.Println() 17 | 18 | for i, ch := range s { // ch is a rune 19 | fmt.Printf("(%d %X) ", i, ch) 20 | } 21 | fmt.Println() 22 | 23 | fmt.Println(utf8.RuneCountInString(s)) 24 | 25 | bytes := []byte(s) 26 | for len(bytes) > 0 { 27 | ch, size := utf8.DecodeRune(bytes) 28 | bytes = bytes[size:] 29 | fmt.Printf("%c ", ch) 30 | } 31 | fmt.Println() 32 | 33 | for i, ch := range []rune(s) { 34 | fmt.Printf("(%d %c) ", i, ch) 35 | } 36 | fmt.Println() 37 | } 38 | -------------------------------------------------------------------------------- /Chapter10/tree/traverse.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import "fmt" 4 | 5 | //func (node *Node) Traverse() { 6 | // if node == nil { 7 | // return 8 | // } 9 | // 10 | // node.Left.Traverse() 11 | // node.Print() 12 | // node.Right.Traverse() 13 | //} 14 | 15 | func (node *Node) Traverse() { 16 | node.TraverseFunc(func(n *Node) { 17 | n.Print() 18 | }) 19 | fmt.Println() 20 | } 21 | 22 | func (node *Node) TraverseFunc(f func(*Node)) { 23 | if node == nil { 24 | return 25 | } 26 | 27 | node.Left.TraverseFunc(f) 28 | f(node) 29 | node.Right.TraverseFunc(f) 30 | } 31 | 32 | 33 | func (node *Node) TraverseWithChannel() chan *Node { 34 | out := make(chan *Node) 35 | go func() { 36 | node.TraverseFunc(func(node *Node){ 37 | out <- node 38 | }) 39 | close(out) 40 | }() 41 | return out 42 | } -------------------------------------------------------------------------------- /Chapter07/filelistingserver/filelisting/handler.go: -------------------------------------------------------------------------------- 1 | package filelisting 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | const prefix = "/list/" 11 | 12 | type userError string 13 | 14 | func (e userError) Error() string { 15 | return e.Message() 16 | } 17 | 18 | func (e userError) Message() string { 19 | return string(e) 20 | } 21 | 22 | func HandleFileList(writer http.ResponseWriter, 23 | request *http.Request) error { 24 | if strings.Index(request.URL.Path, prefix) != 0 { 25 | return userError("Path must start with " + prefix) 26 | } 27 | path := request.URL.Path[len(prefix):] // /list/fib.txt 28 | file, err := os.Open(path) 29 | if err != nil { 30 | return err 31 | } 32 | defer file.Close() 33 | 34 | all, err := ioutil.ReadAll(file) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | writer.Write(all) 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /Chapter08/filelistingserver/filelisting/handler.go: -------------------------------------------------------------------------------- 1 | package filelisting 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | const prefix = "/list/" 11 | 12 | type userError string 13 | 14 | func (e userError) Error() string { 15 | return e.Message() 16 | } 17 | 18 | func (e userError) Message() string { 19 | return string(e) 20 | } 21 | 22 | func HandleFileList(writer http.ResponseWriter, 23 | request *http.Request) error { 24 | if strings.Index(request.URL.Path, prefix) != 0 { 25 | return userError("Path must start with " + prefix) 26 | } 27 | path := request.URL.Path[len(prefix):] // /list/fib.txt 28 | file, err := os.Open(path) 29 | if err != nil { 30 | return err 31 | } 32 | defer file.Close() 33 | 34 | all, err := ioutil.ReadAll(file) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | writer.Write(all) 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /Chapter11/filelistingserver/filelisting/handler.go: -------------------------------------------------------------------------------- 1 | package filelisting 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | const prefix = "/list/" 11 | 12 | type userError string 13 | 14 | func (e userError) Error() string { 15 | return e.Message() 16 | } 17 | 18 | func (e userError) Message() string { 19 | return string(e) 20 | } 21 | 22 | func HandleFileList(writer http.ResponseWriter, 23 | request *http.Request) error { 24 | if strings.Index(request.URL.Path, prefix) != 0 { 25 | return userError("Path must start with " + prefix) 26 | } 27 | path := request.URL.Path[len(prefix):] // /list/fib.txt 28 | file, err := os.Open(path) 29 | if err != nil { 30 | return err 31 | } 32 | defer file.Close() 33 | 34 | all, err := ioutil.ReadAll(file) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | writer.Write(all) 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /Chapter03/maps.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | m := map[string] string { 7 | "name": "ccmouse", 8 | "course": "golang", 9 | "site": "imooc", 10 | "quality": "notbad", 11 | } 12 | 13 | m2 := make(map[string] int) // m2 == empty map 14 | 15 | var m3 map[string] int // m3 == nil 16 | 17 | fmt.Println(m, m2, m3) 18 | 19 | fmt.Println("Traversing map:") 20 | for k, v := range m { 21 | fmt.Println(k, v) 22 | } 23 | 24 | fmt.Println("Getting values:") 25 | courseName, ok := m["course"] 26 | fmt.Println(courseName, ok) 27 | if causeName, ok := m["cause"]; ok{ // 不存在的 key 为 Zero value 28 | fmt.Println(causeName) 29 | } else { 30 | fmt.Println("key does not exist") 31 | } 32 | 33 | fmt.Println("Delete values:") 34 | name, ok := m["name"] 35 | fmt.Println(name, ok) 36 | delete(m, "name") 37 | name, ok = m["name"] 38 | fmt.Println(name, ok) 39 | } 40 | -------------------------------------------------------------------------------- /Chapter11/http/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httputil" 7 | ) 8 | 9 | func main() { 10 | request, err := http.NewRequest(http.MethodGet, 11 | "http://www.baidu.com", nil) 12 | request.Header.Add("User-Agent", 13 | "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1") 14 | client := http.Client{ 15 | CheckRedirect: func( 16 | req *http.Request, 17 | via []*http.Request) error { 18 | fmt.Println("Redirect:", req) 19 | return nil 20 | }, 21 | } 22 | 23 | resp, err := client.Do(request) 24 | 25 | //resp, err := http.Get("http://www.baidu.com") 26 | if err != nil { 27 | panic(err) 28 | } 29 | defer resp.Body.Close() 30 | 31 | s, err := httputil.DumpResponse(resp, true) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | fmt.Printf("%s\n", s) 37 | } 38 | -------------------------------------------------------------------------------- /Chapter13/http/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httputil" 7 | ) 8 | 9 | func main() { 10 | request, err := http.NewRequest(http.MethodGet, 11 | "http://www.baidu.com", nil) 12 | request.Header.Add("User-Agent", 13 | "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1") 14 | 15 | // resp, err := http.DefaultClient.Do(request) 16 | client := http.Client{ 17 | CheckRedirect: func( 18 | req *http.Request, 19 | via []*http.Request) error { 20 | fmt.Println("Redirect:", req) 21 | return nil 22 | }, 23 | } 24 | 25 | resp, err := client.Do(request) 26 | 27 | //resp, err := http.Get("http://www.baidu.com") 28 | if err != nil { 29 | panic(err) 30 | } 31 | defer resp.Body.Close() 32 | 33 | s, err := httputil.DumpResponse(resp, true) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | fmt.Printf("%s\n", s) 39 | } 40 | -------------------------------------------------------------------------------- /Chapter06/functional/fib.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "strings" 8 | ) 9 | 10 | func fibonacci() intGen { 11 | a, b := 0, 1 12 | return func() int { 13 | a, b = b, a+b 14 | return a 15 | } 16 | } 17 | 18 | type intGen func() int 19 | 20 | func (g intGen) Read( 21 | p []byte) (n int, err error) { 22 | next := g() 23 | if next > 10000 { 24 | return 0, io.EOF 25 | } 26 | s := fmt.Sprintf("%d\n", next) 27 | 28 | // TODO: 解决 p 过小导致的问题 29 | return strings.NewReader(s).Read(p) 30 | } 31 | 32 | func printFileContents(reader io.Reader) { 33 | scanner := bufio.NewScanner(reader) 34 | 35 | for scanner.Scan() { 36 | fmt.Println(scanner.Text()) 37 | } 38 | } 39 | 40 | func main() { 41 | f := fibonacci() 42 | 43 | //fmt.Println(f()) // 1 44 | //fmt.Println(f()) // 1 45 | //fmt.Println(f()) // 2 46 | //fmt.Println(f()) // 3 47 | //fmt.Println(f()) // 5 48 | //fmt.Println(f()) // 8 49 | //fmt.Println(f()) // 13 50 | //fmt.Println(f()) // 21 51 | 52 | printFileContents(f) 53 | } 54 | -------------------------------------------------------------------------------- /Chapter03/sliceops.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func printSlice(s []int) { 6 | fmt.Printf("%v, len=%d, cap=%d\n", s, len(s), cap(s)) 7 | } 8 | 9 | func main() { 10 | fmt.Println("Creating slice:" ) 11 | var s []int // Zero value for slice is nil 12 | 13 | for i := 0; i < 100; i++ { 14 | //printSlice(s) 15 | s = append(s, 2 * i + 1) 16 | } 17 | fmt.Println(s) 18 | 19 | s1 := []int{2, 4, 6, 8} 20 | printSlice(s1) 21 | 22 | s2 := make([]int, 16) // length 23 | s3 := make([]int, 10, 32) // length, capacity 24 | printSlice(s2) 25 | printSlice(s3) 26 | 27 | fmt.Println("Copying slice:") 28 | copy(s2, s1) 29 | printSlice(s2) 30 | 31 | fmt.Println("Deleting elements from slice:") 32 | s2 = append(s2[:3], s2[4:]...) 33 | printSlice(s2) 34 | 35 | fmt.Println("Popping from front:") 36 | front := s2[0] 37 | s2 = s2[1:] 38 | fmt.Println(front) 39 | printSlice(s2) 40 | 41 | fmt.Println("Popping from back:") 42 | tail := s2[len(s2) - 1] 43 | s2 = s2[:len(s2) -1] 44 | fmt.Println(tail) 45 | printSlice(s2) 46 | } 47 | -------------------------------------------------------------------------------- /Chapter13/http/gindemo/ginserver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math/rand" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/gin-gonic/gin" 9 | "go.uber.org/zap" 10 | ) 11 | 12 | const keyRequestId = "requestId" 13 | 14 | func main() { 15 | r := gin.Default() 16 | logger, err := zap.NewProduction() 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | r.Use(func(c *gin.Context) { 22 | s := time.Now() 23 | c.Next() 24 | 25 | // path, status, log latency 26 | logger.Info("incoming request", 27 | zap.String("path", c.Request.URL.Path), 28 | zap.Int("status", c.Writer.Status()), 29 | zap.Duration("elapsed", time.Now().Sub(s)), 30 | ) 31 | }, func(c *gin.Context) { 32 | c.Set(keyRequestId, rand.Int()) 33 | c.Next() 34 | }) 35 | 36 | r.GET("/ping", func(c *gin.Context) { 37 | h := gin.H{ 38 | "message": "pong", 39 | } 40 | if rid, exists := c.Get(keyRequestId); exists { 41 | h[keyRequestId] = rid 42 | } 43 | c.JSON(200, h) 44 | }) 45 | r.GET("/hello", func(c *gin.Context) { 46 | c.String(http.StatusOK, "hello") 47 | }) 48 | r.Run() 49 | } 50 | -------------------------------------------------------------------------------- /Chapter08/nonrepeatingsubstr/nonrepeating_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestSubstr(t *testing.T) { 6 | tests := []struct{ 7 | s string 8 | ans int 9 | } { 10 | // Normal cases 11 | {"abcabcbb", 3}, 12 | {"pwwkew", 3}, 13 | 14 | // Edge Cases 15 | {"", 0}, 16 | {"b", 1}, 17 | {"bbbbbbbb", 1}, 18 | {"abcabcabcd", 4}, 19 | 20 | // Chinese support 21 | {"这里是慕课网", 6}, 22 | {"一二三一二", 3}, 23 | {"黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花", 8}, 24 | } 25 | 26 | for _, tt := range tests { 27 | actual := lengthOfNonRepeatingSubStr(tt.s) 28 | if actual != tt.ans { 29 | t.Errorf("Got %d for input %s; " + 30 | "expected %d", 31 | actual, tt.s, tt.ans) 32 | } 33 | } 34 | } 35 | 36 | func BenchmarkSubstr(b *testing.B) { 37 | s := "黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花" 38 | for i := 0; i < 13; i++ { 39 | s = s + s 40 | } 41 | b.Logf("len(s) = %d", len(s)) 42 | ans := 8 43 | b.ResetTimer() 44 | 45 | for i := 0; i < b.N; i++ { 46 | actual := lengthOfNonRepeatingSubStr(s) 47 | if actual != ans { 48 | b.Errorf("Got %d for input %s; " + 49 | "expected %d", 50 | actual, s, ans) 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Chapter02/loop/loop.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // 整数转二进制 13 | func convertToBin(n int) string { 14 | result := "" 15 | for ; n > 0; n /= 2 { 16 | lsb := n % 2 17 | result = strconv.Itoa(lsb) + result 18 | } 19 | return result 20 | } 21 | 22 | func printFile(filename string) { 23 | file, err := os.Open(filename) 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | printFileContents(file) 29 | } 30 | 31 | func printFileContents(reader io.Reader) { 32 | scanner := bufio.NewScanner(reader) 33 | 34 | for scanner.Scan() { 35 | fmt.Println(scanner.Text()) 36 | } 37 | } 38 | 39 | // 死循环 40 | func forever() { 41 | for { 42 | fmt.Println("abc") 43 | } 44 | } 45 | 46 | func main() { 47 | //sum := 0 48 | //for i := 1; i <= 100; i++ { 49 | // sum += i 50 | //} 51 | fmt.Println( 52 | convertToBin(5), // 101 53 | convertToBin(13), // 1101 54 | convertToBin(789798), 55 | convertToBin(0), 56 | ) 57 | 58 | printFile("Chapter02/branch/abc.txt") 59 | 60 | s := `abc"d" 61 | kkkk 62 | 123 63 | 64 | p` 65 | printFileContents(strings.NewReader(s)) 66 | 67 | //forever() 68 | } 69 | -------------------------------------------------------------------------------- /Chapter03/nonrepeating.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func lengthOfNonRepeatingSubStr(s string) int{ 6 | //lastOccurred := make(map [byte] int) 7 | lastOccurred := make(map [rune] int) 8 | start := 0 9 | maxLength := 0 10 | 11 | //for i, ch := range []byte(s) { 12 | for i, ch := range []rune(s) { 13 | if lastI, ok := lastOccurred[ch]; ok && lastI >= start { 14 | start = lastI + 1 15 | } 16 | if i - start + 1 > maxLength{ 17 | maxLength = i - start + 1 18 | } 19 | lastOccurred[ch] = i 20 | } 21 | 22 | return maxLength 23 | } 24 | 25 | func main() { 26 | fmt.Println( 27 | lengthOfNonRepeatingSubStr("abcabcbb")) 28 | fmt.Println( 29 | lengthOfNonRepeatingSubStr("bbb")) 30 | fmt.Println( 31 | lengthOfNonRepeatingSubStr("pwwkew")) 32 | fmt.Println( 33 | lengthOfNonRepeatingSubStr("")) 34 | fmt.Println( 35 | lengthOfNonRepeatingSubStr("b")) 36 | fmt.Println( 37 | lengthOfNonRepeatingSubStr("abcdef")) 38 | // 使用 byte 在处理中文时会存在问题,可修改为使用 rune 39 | fmt.Println( 40 | lengthOfNonRepeatingSubStr("这里是慕课网")) 41 | fmt.Println( 42 | lengthOfNonRepeatingSubStr("一二三一二")) 43 | fmt.Println( 44 | lengthOfNonRepeatingSubStr( 45 | "黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花")) 46 | } 47 | -------------------------------------------------------------------------------- /Chapter10/done/done.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func doWork(id int, w worker) { 9 | for { 10 | for n := range w.in { 11 | fmt.Printf("Worker %d received %c\n", 12 | id, n) 13 | //go func() { 14 | // done <- true 15 | //}() 16 | w.done() 17 | } 18 | } 19 | } 20 | 21 | type worker struct { 22 | in chan int 23 | done func() 24 | //wg *sync.WaitGroup 25 | //done chan bool 26 | } 27 | 28 | func createWorker(id int, wg *sync.WaitGroup) worker { 29 | w := worker{ 30 | in: make(chan int), 31 | done: func() { 32 | wg.Done() 33 | }, 34 | } 35 | 36 | go doWork(id, w) 37 | return w 38 | } 39 | 40 | func chanDemo() { 41 | var wg sync.WaitGroup 42 | wg.Add(20) 43 | 44 | var workers [10]worker 45 | for i := 0; i < 10; i++ { 46 | workers[i] = createWorker(i, &wg) 47 | } 48 | 49 | for i, worker := range workers { 50 | worker.in <- 'a' + i 51 | } 52 | 53 | for i, worker := range workers { 54 | worker.in <- 'A' + i 55 | } 56 | 57 | wg.Done() 58 | 59 | wg.Wait() 60 | 61 | // wait for all of them 62 | //for _, worker := range workers { 63 | // <- worker.done 64 | // <- worker.done 65 | //} 66 | } 67 | 68 | func main(){ 69 | chanDemo() 70 | } 71 | -------------------------------------------------------------------------------- /Chapter03/slices.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func updateSlice(s []int){ 6 | s[0] = 100 7 | } 8 | 9 | func main() { 10 | arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7} 11 | 12 | s := arr[2:6] 13 | fmt.Println("arr[2:6] =", s) 14 | fmt.Println("arr[:6] =", arr[:6]) 15 | s1 := arr[2:] 16 | fmt.Println("s1 =", s1) 17 | s2 := arr[:] 18 | fmt.Println("s2 =", s2) 19 | 20 | fmt.Println("After updateSlice(s1):") 21 | updateSlice(s1) 22 | fmt.Println(s1) 23 | fmt.Println(arr) 24 | 25 | fmt.Println("After updateSlice(s2):") 26 | updateSlice(s2) 27 | fmt.Println(s2) 28 | fmt.Println(arr) 29 | 30 | fmt.Println("Reslice:") 31 | fmt.Println(s2) 32 | s2 = s2[:5] 33 | fmt.Println(s2) 34 | s2 = s2[2:] 35 | fmt.Println(s2) 36 | 37 | fmt.Println("Extending slice:") 38 | arr[0], arr[2] = 0, 2 39 | fmt.Println("arr = ", arr) 40 | s1 = arr[2:6] 41 | s2 = s1[3:5] 42 | fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n", 43 | s1, len(s1), cap(s1)) 44 | fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n", 45 | s2, len(s2), cap(s2)) 46 | //fmt.Println(s1[3:7]) 47 | 48 | s3 := append(s2, 10) 49 | s4 := append(s3, 11) 50 | s5 := append(s4, 12) 51 | fmt.Println("s3, s4, s5 =", s3, s4, s5) 52 | // s4和 s5不再是对 arr 的视图 53 | fmt.Println("arr =", arr) 54 | } 55 | -------------------------------------------------------------------------------- /Chapter07/errhandling/defer/defer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "learninggo/Chapter07/errhandling/fib" 7 | "os" 8 | ) 9 | 10 | func tryDefer() { 11 | //defer fmt.Println(1) 12 | //defer fmt.Println(2) 13 | //fmt.Println(3) 14 | //panic("error occured") 15 | //fmt.Println(4) 16 | 17 | for i := 0; i < 100; i++ { 18 | defer fmt.Println(i) 19 | if i == 30 { 20 | panic("printed too many") 21 | } 22 | } 23 | } 24 | 25 | func writeFile(filename string) { 26 | //file, err := os.Create(filename) 27 | file, err := os.OpenFile( 28 | filename, os.O_EXCL|os.O_CREATE, 0666) 29 | 30 | //err = errors.New("this is a custom error") 31 | 32 | if err != nil { 33 | if pathError, ok := err.(*os.PathError); !ok { 34 | panic(err) 35 | } else { 36 | fmt.Printf("%s, %s, %s\n", 37 | pathError.Op, 38 | pathError.Path, 39 | pathError.Err) 40 | } 41 | //fmt.Println("Error:", err.Error()) 42 | return 43 | //panic(err) 44 | } 45 | defer file.Close() 46 | 47 | writer := bufio.NewWriter(file) 48 | defer writer.Flush() 49 | 50 | f := fib.Fibonacci() 51 | for i := 0; i < 20; i++ { 52 | fmt.Fprintln(writer, f()) 53 | } 54 | } 55 | 56 | func main() { 57 | //tryDefer() 58 | writeFile("fib.txt") 59 | } 60 | -------------------------------------------------------------------------------- /Chapter04/tree/entry/entry.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "learninggo/Chapter04/tree" 6 | 7 | "golang.org/x/tools/container/intsets" 8 | ) 9 | 10 | type myTreeNode struct { 11 | node *tree.Node 12 | } 13 | 14 | func (myNode *myTreeNode) postOrder() { 15 | if myNode == nil || myNode.node == nil { 16 | return 17 | } 18 | 19 | left := myTreeNode{myNode.node.Left} 20 | left.postOrder() 21 | right := myTreeNode{myNode.node.Right} 22 | right.postOrder() 23 | myNode.node.Print() 24 | } 25 | 26 | func testSparse() { 27 | s := intsets.Sparse{} 28 | 29 | s.Insert(1) 30 | s.Insert(1000) 31 | s.Insert(1000000) 32 | fmt.Println(s.Has(1000)) 33 | fmt.Println(s.Has(10000)) 34 | } 35 | 36 | func main() { 37 | var root tree.Node 38 | 39 | root = tree.Node{Value: 3} 40 | root.Left = &tree.Node{} 41 | root.Right = &tree.Node{5, nil, nil} 42 | root.Right.Left = new(tree.Node) 43 | root.Left.Right = tree.CreateNode(2) 44 | 45 | root.Right.Left.SetValue(4) 46 | 47 | root.Traverse() 48 | //fmt.Println() 49 | //myRoot := myTreeNode{&root} 50 | //myRoot.postOrder() 51 | //fmt.Println() 52 | // 53 | //testSparse() 54 | 55 | nodeCount := 0 56 | root.TraverseFunc(func(node *tree.Node) { 57 | nodeCount++ 58 | }) 59 | fmt.Println("Node count:", nodeCount) 60 | } 61 | -------------------------------------------------------------------------------- /Chapter03/arrays.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func printArray(arr *[5]int) { 6 | arr[0] = 100 7 | for i, v := range arr { 8 | fmt.Println(i, v) 9 | } 10 | } 11 | 12 | func main() { 13 | var arr1 [5]int // 声明数组 14 | arr2 := [3]int{1, 3, 5} // 声明数组并赋值 15 | arr3 := [...]int{2, 4, 6, 8, 10} // 不输入数组长度,让编译器来计算长度 16 | var grid [4][5]int // 二维数组 17 | fmt.Println(arr1, arr2, arr3) 18 | fmt.Println(grid) 19 | 20 | //for i := 0; i < len(arr3); i++ { 21 | // fmt.Println(arr3[i]) 22 | //} 23 | 24 | // 遍历数组,通过下标来获取值 25 | for i := range arr3 { 26 | fmt.Println(arr3[i]) 27 | } 28 | 29 | // 遍历数组,获取下标和值 30 | for i, v := range arr3 { 31 | fmt.Println(i, v) 32 | } 33 | 34 | // 遍历数组,只打印值 35 | for _, v := range arr3 { 36 | fmt.Println(v) 37 | } 38 | 39 | 40 | //numbers := [6]int{1, 3, 2, 5, 8, 4} 41 | //for i := 0; i < len(numbers); i++ { 42 | // fmt.Println(numbers[i]) 43 | //} 44 | // 45 | //maxi := -1 46 | //maxValue := -1 47 | //for i, v := range numbers { 48 | // if v > maxValue { 49 | // maxi, maxValue = i, v 50 | // } 51 | //} 52 | //fmt.Println(maxi, maxValue) 53 | fmt.Println("printArray(arr1):") 54 | printArray(&arr1) 55 | 56 | fmt.Println("printArray(arr3):") 57 | printArray(&arr3) 58 | 59 | fmt.Println("fmt.Println(arr1, arr3):") 60 | fmt.Println(arr1, arr3) 61 | } 62 | -------------------------------------------------------------------------------- /Chapter04/node.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type treeNode struct { 6 | value int 7 | left, right *treeNode 8 | } 9 | 10 | func (node treeNode) print(){ 11 | fmt.Print(node.value, " ") 12 | } 13 | 14 | func (node *treeNode) setValue(value int) { 15 | if node == nil { 16 | fmt.Println("Setting value to nil node. Ignored!") 17 | return 18 | } 19 | node.value = value 20 | } 21 | 22 | func (node *treeNode) tranverse() { 23 | if node == nil { 24 | return 25 | } 26 | 27 | node.left.tranverse() 28 | node.print() 29 | node.right.tranverse() 30 | } 31 | 32 | func createNode(value int) *treeNode { 33 | return &treeNode{value: value} 34 | } 35 | 36 | func main() { 37 | var root treeNode 38 | 39 | root = treeNode{value: 3} 40 | root.left = &treeNode{} 41 | root.right = &treeNode{5, nil, nil} 42 | root.right.left = new(treeNode) 43 | root.left.right = createNode(2) 44 | 45 | //root.print() 46 | root.right.left.setValue(4) 47 | 48 | root.tranverse() 49 | //root.right.left.print() 50 | //fmt.Println() 51 | // 52 | //root.setValue(100) 53 | // 54 | //var pRoot *treeNode 55 | //pRoot.setValue(200) 56 | //pRoot = &root 57 | //pRoot.setValue(300) 58 | //pRoot.print() 59 | 60 | //nodes := []treeNode { 61 | // {value: 3}, 62 | // {}, 63 | // {6, nil, &root}, 64 | //} 65 | // 66 | //fmt.Println(root) 67 | //fmt.Println(nodes) 68 | } 69 | -------------------------------------------------------------------------------- /Chapter02/branch/branch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | ) 7 | 8 | //func bounded(v int) int { 9 | // if v > 100 { 10 | // return 100 11 | // } else if v < 0 { 12 | // return 0 13 | // } else { 14 | // return v 15 | // } 16 | //} 17 | 18 | //func eval(a, b int, op string) int { 19 | // var result int 20 | // switch op { 21 | // case "+": 22 | // result = a + b 23 | // case "-": 24 | // result = a - b 25 | // case "*": 26 | // result = a * b 27 | // case "/": 28 | // result = a / b 29 | // default: 30 | // panic("unsupported operator:" + op) 31 | // } 32 | // return result 33 | //} 34 | 35 | func grade(score int) string { 36 | g := "" 37 | switch { 38 | case score < 0 || score > 100: 39 | panic(fmt.Sprintf("Wrong score: %d", score)) 40 | case score < 60: 41 | g = "F" 42 | case score < 80: 43 | g = "C" 44 | case score < 90: 45 | g = "B" 46 | case score <= 100: 47 | g = "A" 48 | } 49 | return g 50 | } 51 | 52 | func main() { 53 | const filename = "abc.txt" 54 | //contents, err := ioutil.ReadFile(filename) 55 | //if err != nil { 56 | // fmt.Print(err) 57 | //} else { 58 | // fmt.Printf("%s\n", contents) 59 | //} 60 | 61 | if contents, err := ioutil.ReadFile(filename); err != nil { 62 | fmt.Println(err) 63 | } else { 64 | fmt.Printf("%s\n", contents) 65 | } 66 | 67 | fmt.Println( 68 | grade(0), 69 | grade(59), 70 | grade(60), 71 | grade(82), 72 | grade(99), 73 | grade(100), 74 | grade(101), 75 | ) 76 | } 77 | -------------------------------------------------------------------------------- /Chapter10/tree/entry/entry.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "learninggo/Chapter10/tree" 6 | 7 | "golang.org/x/tools/container/intsets" 8 | ) 9 | 10 | type myTreeNode struct { 11 | node *tree.Node 12 | } 13 | 14 | func (myNode *myTreeNode) postOrder() { 15 | if myNode == nil || myNode.node == nil { 16 | return 17 | } 18 | 19 | left := myTreeNode{myNode.node.Left} 20 | left.postOrder() 21 | right := myTreeNode{myNode.node.Right} 22 | right.postOrder() 23 | myNode.node.Print() 24 | } 25 | 26 | func testSparse() { 27 | s := intsets.Sparse{} 28 | 29 | s.Insert(1) 30 | s.Insert(1000) 31 | s.Insert(1000000) 32 | fmt.Println(s.Has(1000)) 33 | fmt.Println(s.Has(10000)) 34 | } 35 | 36 | func main() { 37 | var root tree.Node 38 | 39 | root = tree.Node{Value: 3} 40 | root.Left = &tree.Node{} 41 | root.Right = &tree.Node{5, nil, nil} 42 | root.Right.Left = new(tree.Node) 43 | root.Left.Right = tree.CreateNode(2) 44 | 45 | root.Right.Left.SetValue(4) 46 | 47 | root.Traverse() 48 | //fmt.Println() 49 | //myRoot := myTreeNode{&root} 50 | //myRoot.postOrder() 51 | //fmt.Println() 52 | // 53 | //testSparse() 54 | 55 | nodeCount := 0 56 | root.TraverseFunc(func(node *tree.Node) { 57 | nodeCount++ 58 | }) 59 | fmt.Println("Node count:", nodeCount) 60 | 61 | c := root.TraverseWithChannel() 62 | maxNode := 0 63 | for node := range c { 64 | if node.Value > maxNode { 65 | maxNode = node.Value 66 | } 67 | } 68 | fmt.Println("Max node value:", maxNode) 69 | } 70 | -------------------------------------------------------------------------------- /Chapter10/channel/channel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func worker(id int, c chan int) { 9 | for { 10 | //n, ok := <-c 11 | //if !ok { 12 | // break 13 | //} 14 | //fmt.Printf("Worker %d received %d\n", 15 | // id, n) 16 | 17 | for n := range c { 18 | fmt.Printf("Worker %d received %d\n", 19 | id, n) 20 | } 21 | } 22 | } 23 | 24 | func createWorker(id int) chan<- int { 25 | c := make(chan int) 26 | go worker(id, c) 27 | 28 | return c 29 | } 30 | 31 | func chanDemo() { 32 | var channels [10]chan <- int 33 | for i := 0; i < 10; i++ { 34 | channels[i] = make(chan int) 35 | channels[i] = createWorker(i) 36 | } 37 | 38 | for i := 0; i < 10; i++ { 39 | channels[i] <- 'a' + i 40 | } 41 | 42 | for i := 0; i < 10; i++ { 43 | channels[i] <- 'A' + i 44 | } 45 | 46 | time.Sleep(time.Millisecond) 47 | } 48 | 49 | func bufferedChannel() { 50 | c := make(chan int, 3) 51 | go worker(0, c) 52 | 53 | c <- 'a' 54 | c <- 'b' 55 | c <- 'c' 56 | c <- 'd' 57 | 58 | time.Sleep(time.Millisecond) 59 | } 60 | 61 | func channelClose() { 62 | c := make(chan int, 3) 63 | go worker(0, c) 64 | 65 | c <- 'a' 66 | c <- 'b' 67 | c <- 'c' 68 | c <- 'd' 69 | close(c) 70 | 71 | time.Sleep(time.Millisecond) 72 | } 73 | 74 | func main(){ 75 | fmt.Println("Channel as 1st class citizen:") 76 | chanDemo() 77 | fmt.Println("Buffered channel:") 78 | bufferedChannel() 79 | fmt.Println("Channel close and range:") 80 | channelClose() 81 | } 82 | -------------------------------------------------------------------------------- /Chapter07/filelistingserver/web.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "learninggo/Chapter07/filelistingserver/filelisting" 5 | "log" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | type appHandler func(writer http.ResponseWriter, 11 | request *http.Request) error 12 | 13 | func errWrapper( 14 | handler appHandler) func( 15 | http.ResponseWriter, *http.Request) { 16 | return func(writer http.ResponseWriter, 17 | request *http.Request) { 18 | defer func() { 19 | if r := recover(); r != nil { 20 | log.Printf("Panic: %v", r) 21 | http.Error(writer, 22 | http.StatusText(http.StatusInternalServerError), 23 | http.StatusInternalServerError) 24 | } 25 | }() 26 | err := handler(writer, request) 27 | 28 | if err != nil { 29 | log.Printf("Error occured "+ 30 | "handling request: %s", 31 | err.Error()) 32 | 33 | if userErr, ok := err.(userError); ok { 34 | http.Error(writer, userErr.Message(), 35 | http.StatusBadRequest) 36 | return 37 | } 38 | 39 | code := http.StatusOK 40 | switch { 41 | case os.IsNotExist(err): 42 | code = http.StatusNotFound 43 | case os.IsPermission(err): 44 | code = http.StatusForbidden 45 | default: 46 | code = http.StatusInternalServerError 47 | } 48 | http.Error( 49 | writer, 50 | http.StatusText(code), 51 | code) 52 | } 53 | } 54 | } 55 | 56 | type userError interface { 57 | error 58 | Message() string 59 | } 60 | 61 | func main() { 62 | //http.HandleFunc("/list/", 63 | http.HandleFunc("/", 64 | errWrapper(filelisting.HandleFileList)) 65 | 66 | err := http.ListenAndServe(":9999", nil) 67 | if err != nil { 68 | panic(err) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Chapter08/filelistingserver/web.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "learninggo/Chapter07/filelistingserver/filelisting" 5 | "log" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | type appHandler func(writer http.ResponseWriter, 11 | request *http.Request) error 12 | 13 | func errWrapper( 14 | handler appHandler) func( 15 | http.ResponseWriter, *http.Request) { 16 | return func(writer http.ResponseWriter, 17 | request *http.Request) { 18 | defer func() { 19 | if r := recover(); r != nil { 20 | log.Printf("Panic: %v", r) 21 | http.Error(writer, 22 | http.StatusText(http.StatusInternalServerError), 23 | http.StatusInternalServerError) 24 | } 25 | }() 26 | err := handler(writer, request) 27 | 28 | if err != nil { 29 | log.Printf("Error occured "+ 30 | "handling request: %s", 31 | err.Error()) 32 | 33 | // user error 34 | if userErr, ok := err.(userError); ok { 35 | http.Error(writer, userErr.Message(), 36 | http.StatusBadRequest) 37 | return 38 | } 39 | 40 | // system error 41 | code := http.StatusOK 42 | switch { 43 | case os.IsNotExist(err): 44 | code = http.StatusNotFound 45 | case os.IsPermission(err): 46 | code = http.StatusForbidden 47 | default: 48 | code = http.StatusInternalServerError 49 | } 50 | http.Error( 51 | writer, 52 | http.StatusText(code), 53 | code) 54 | } 55 | } 56 | } 57 | 58 | type userError interface { 59 | error 60 | Message() string 61 | } 62 | 63 | func main() { 64 | //http.HandleFunc("/list/", 65 | http.HandleFunc("/", 66 | errWrapper(filelisting.HandleFileList)) 67 | 68 | err := http.ListenAndServe(":9999", nil) 69 | if err != nil { 70 | panic(err) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Chapter11/filelistingserver/web.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "learninggo/Chapter11/filelistingserver/filelisting" 5 | "log" 6 | "net/http" 7 | "os" 8 | _ "net/http/pprof" 9 | ) 10 | 11 | type appHandler func(writer http.ResponseWriter, 12 | request *http.Request) error 13 | 14 | func errWrapper( 15 | handler appHandler) func( 16 | http.ResponseWriter, *http.Request) { 17 | return func(writer http.ResponseWriter, 18 | request *http.Request) { 19 | defer func() { 20 | if r := recover(); r != nil { 21 | log.Printf("Panic: %v", r) 22 | http.Error(writer, 23 | http.StatusText(http.StatusInternalServerError), 24 | http.StatusInternalServerError) 25 | } 26 | }() 27 | err := handler(writer, request) 28 | 29 | if err != nil { 30 | log.Printf("Error occured "+ 31 | "handling request: %s", 32 | err.Error()) 33 | 34 | // user error 35 | if userErr, ok := err.(userError); ok { 36 | http.Error(writer, userErr.Message(), 37 | http.StatusBadRequest) 38 | return 39 | } 40 | 41 | // system error 42 | code := http.StatusOK 43 | switch { 44 | case os.IsNotExist(err): 45 | code = http.StatusNotFound 46 | case os.IsPermission(err): 47 | code = http.StatusForbidden 48 | default: 49 | code = http.StatusInternalServerError 50 | } 51 | http.Error( 52 | writer, 53 | http.StatusText(code), 54 | code) 55 | } 56 | } 57 | } 58 | 59 | type userError interface { 60 | error 61 | Message() string 62 | } 63 | 64 | func main() { 65 | //http.HandleFunc("/list/", 66 | http.HandleFunc("/", 67 | errWrapper(filelisting.HandleFileList)) 68 | 69 | err := http.ListenAndServe(":9999", nil) 70 | if err != nil { 71 | panic(err) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Chapter10/select/select.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func generator() chan int { 10 | out := make(chan int) 11 | go func() { 12 | i := 0 13 | for { 14 | time.Sleep(time.Duration(rand.Intn(1500)) * 15 | time.Millisecond) 16 | out <- i 17 | i++ 18 | } 19 | 20 | }() 21 | return out 22 | } 23 | 24 | func worker(id int, c chan int) { 25 | for { 26 | for n := range c { 27 | time.Sleep(time.Second) 28 | fmt.Printf("Worker %d received %d\n", 29 | id, n) 30 | } 31 | } 32 | } 33 | 34 | func createWorker(id int) chan<- int { 35 | c := make(chan int) 36 | go worker(id, c) 37 | 38 | return c 39 | } 40 | 41 | func main() { 42 | var c1, c2 = generator(), generator() 43 | //w := createWorker(0) 44 | var worker = createWorker(0) 45 | 46 | var values []int 47 | //n := 0 48 | //hasValue := false 49 | tm := time.After(10 * time.Second) 50 | tick := time.Tick(time.Second) 51 | for { 52 | var activeWorker chan <-int 53 | var activeValue int 54 | if len(values) > 0 { 55 | activeWorker = worker 56 | activeValue = values[0] 57 | } 58 | select { 59 | case n := <-c1: 60 | values = append(values, n) 61 | case n := <-c2: 62 | values = append(values, n) 63 | case activeWorker <- activeValue: 64 | values = values[1:] 65 | case <- time.After(800 * time.Millisecond): 66 | fmt.Println("timeout") 67 | case <- tick: 68 | fmt.Println("que len = ", len(values)) 69 | case <- tm: 70 | fmt.Println("bye") 71 | return 72 | //case n := <-c1: 73 | // w <- n 74 | //fmt.Println("Received from c1:", n) 75 | //case n := <-c2: 76 | // w <- n 77 | //fmt.Println("Received from c2:", n) 78 | //default: 79 | // fmt.Println("No value received") 80 | //} 81 | } 82 | 83 | } 84 | } -------------------------------------------------------------------------------- /Chapter02/basic/basic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/cmplx" 7 | ) 8 | 9 | var ( 10 | aa = 3 11 | ss = "kkk" 12 | bb = true 13 | ) 14 | 15 | //函数外不能使用 := 来进行赋值 16 | 17 | //定义变量 18 | func variableZeroValue() { 19 | var a int 20 | var s string 21 | fmt.Printf("%d %q\n", a, s) 22 | } 23 | 24 | //变量赋初值 25 | func variableInitialValue() { 26 | var a, b int = 3, 4 27 | var s string = "abc" 28 | fmt.Println(a, b, s) 29 | } 30 | 31 | func variableTypeDeduction() { 32 | var a, b, c, s = 3, 4, true, "def" 33 | fmt.Println(a, b, c, s) 34 | } 35 | 36 | func variableShort() { 37 | a, b, c, s := 3, 4, true, "def" 38 | b = 5 39 | fmt.Println(a, b, c, s) 40 | } 41 | 42 | // 欧拉公式 43 | func euler() { 44 | //c := 3 + 4i 45 | //fmt.Println(cmplx.Abs(c)) 46 | fmt.Println(cmplx.Exp(1i*math.Pi) + 1) 47 | // 与上面一句效果相同:fmt.Println(cmplx.Pow(math.E, 1i * math.Pi) + 1) 48 | fmt.Printf("%0.3f\n", cmplx.Exp(1i*math.Pi)+1) //浮点数不精确,取0的方法 49 | } 50 | 51 | // 类型转换 52 | func triangle() { 53 | var a, b int = 3, 4 54 | var c int 55 | c = int(math.Sqrt(float64(a*a + b*b))) 56 | fmt.Println(c) 57 | } 58 | 59 | // 常量 60 | func consts() { 61 | const ( 62 | filename = "abc.txt" 63 | a, b = 3, 4 64 | ) 65 | var c = int(math.Sqrt(a*a + b*b)) // a,b 未指定类型无需转换为 float 66 | fmt.Println(filename, c) 67 | } 68 | 69 | // 枚举类型 70 | func enums() { 71 | const ( 72 | cpp = iota // 用于自增,可不再为下面的赋值 73 | java 74 | python 75 | golang 76 | ) 77 | 78 | const ( 79 | b = 1 << (10 * iota) 80 | kb 81 | mb 82 | gb 83 | tb 84 | pb 85 | ) 86 | fmt.Println(cpp, java, python, golang) 87 | fmt.Println(b, kb, mb, gb, tb, pb) 88 | } 89 | 90 | func main() { 91 | fmt.Println("Hello world") 92 | variableZeroValue() 93 | variableInitialValue() 94 | variableTypeDeduction() 95 | variableShort() 96 | fmt.Println(aa, bb, ss) 97 | euler() 98 | triangle() 99 | consts() 100 | enums() 101 | } 102 | -------------------------------------------------------------------------------- /Chapter05/retriever/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "learninggo/Chapter05/retriever/mock" 6 | "learninggo/Chapter05/retriever/real" 7 | "time" 8 | ) 9 | 10 | type Retriever interface { 11 | Get(url string) string 12 | } 13 | 14 | type Poster interface { 15 | Post(url string, 16 | form map[string]string) string 17 | } 18 | 19 | const url = "http://www.baidu.com" 20 | 21 | func download(r Retriever) string { 22 | return r.Get(url) 23 | } 24 | 25 | func post(poster Poster) { 26 | poster.Post(url, 27 | map[string]string{ 28 | "name": "ccmouse", 29 | "course": "golang", 30 | }) 31 | } 32 | 33 | type RetrieverPoster interface { 34 | Retriever 35 | Poster 36 | } 37 | 38 | func session(s RetrieverPoster) string { 39 | s.Post(url, 40 | map[string]string{ 41 | "contents": "another fake " + url, 42 | }) 43 | return s.Get(url) 44 | } 45 | 46 | func main() { 47 | var r Retriever 48 | retriever := mock.Retriever{"this is a fake " + url} 49 | r = &retriever 50 | inspect(r) 51 | 52 | r = &real.Retriever{ 53 | UserAgent: "Mozilla/5.0", 54 | TimeOut: time.Minute, 55 | } 56 | fmt.Printf("%T %v\n", r, r) 57 | 58 | inspect(r) 59 | 60 | // Type assertion 61 | //realRetriever := r.(*real.Retriever) 62 | //fmt.Println(realRetriever.TimeOut) 63 | 64 | if mockRetriever, ok := r.(*mock.Retriever); ok { 65 | fmt.Println(mockRetriever.Contents) 66 | } else { 67 | fmt.Println("not a mock retriever") 68 | } 69 | 70 | //fmt.Println(download(r)) 71 | fmt.Println("Try a session:") 72 | fmt.Println(session(&retriever)) 73 | 74 | //fmt.Println(download( 75 | // mock.Retriever{ 76 | // "this is a fake baidu.com"})) 77 | } 78 | 79 | func inspect(r Retriever) { 80 | fmt.Println("Inspecting", r) 81 | fmt.Printf(" > %T %v\n", r, r) 82 | fmt.Print(" > Type switch:") 83 | switch v := r.(type) { 84 | case *mock.Retriever: 85 | fmt.Println("Contents:", v.Contents) 86 | case *real.Retriever: 87 | fmt.Println("UserAgent:", v.UserAgent) 88 | 89 | } 90 | fmt.Println() 91 | } 92 | -------------------------------------------------------------------------------- /Chapter02/func/func.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "reflect" 7 | "runtime" 8 | ) 9 | 10 | func eval(a, b int, op string) (int, error) { 11 | switch op { 12 | case "+": 13 | return a + b, nil 14 | case "-": 15 | return a - b, nil 16 | case "*": 17 | return a * b, nil 18 | case "/": 19 | //return a / b 20 | q, _ := div(a, b) // 下划线表示接收的变量无需使用 21 | return q, nil 22 | default: 23 | //panic("unsupported operation: " + op) 24 | return 0, fmt.Errorf( 25 | "unsupported operation: %s", op) 26 | } 27 | } 28 | 29 | // 函数返回多个值 带余除法:13 / 3 = 4 ... 1 30 | //func div(a, b int) (int, int) { 31 | // return a / b, a % b 32 | //} 33 | func div(a, b int) (q, r int) { 34 | q = a / b 35 | r = a % b 36 | return 37 | } 38 | 39 | //func apply(op func(int, int) int, a, b int) int { 40 | // p := reflect.ValueOf(op).Pointer() 41 | // opName := runtime.FuncForPC(p).Name() 42 | // fmt.Printf("Calling function %s with args " + 43 | // "(%d, %d)\n", opName, a, b) 44 | // return op(a, b) 45 | //} 46 | 47 | func pow(a, b int) int { 48 | return int(math.Pow(float64(a), float64(b))) 49 | } 50 | 51 | func apply(op func(int, int) int, a, b int) int { 52 | fmt.Printf("Calling %s with %d, %d\n", 53 | runtime.FuncForPC(reflect.ValueOf(op).Pointer()).Name(), 54 | a, b) 55 | return op(a, b) 56 | } 57 | 58 | func sumArgs(values ...int) int { 59 | sum := 0 60 | for i := range values { 61 | sum += values[i] 62 | } 63 | return sum 64 | } 65 | 66 | //func swap(a, b *int){ 67 | // *b, *a = *a, *b 68 | //} 69 | func swap(a, b int) (int, int) { 70 | return b, a 71 | } 72 | 73 | func main() { 74 | //fmt.Println(eval(3, 4, "x")) 75 | if result, err := eval(3, 4, "x"); err != nil { 76 | fmt.Println("Error:", err) 77 | } else { 78 | fmt.Println(result) 79 | } 80 | //fmt.Println(div(13, 3)) 81 | q, r := div(13, 3) 82 | fmt.Println(q, r) 83 | 84 | //fmt.Println(apply(pow, 3, 4)) 85 | fmt.Println(apply( 86 | func(a int, b int) int { 87 | return int(math.Pow(float64(a), float64(b))) 88 | }, 3, 4)) 89 | 90 | fmt.Println(sumArgs(1, 2, 3, 4, 5)) 91 | a, b := 3, 4 92 | //swap(&a, &b) 93 | a, b = swap(a, b) 94 | fmt.Println(a, b) 95 | } 96 | -------------------------------------------------------------------------------- /Chapter12/maze/maze.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func readMaze(filename string) [][]int { 9 | file, err := os.Open(filename) 10 | if err != nil { 11 | panic(err) 12 | } 13 | var row, col int 14 | fmt.Fscanf(file, "%d %d", &row, &col) 15 | 16 | maze := make([][]int, row) 17 | for i := range maze { 18 | maze[i] = make([]int, col) 19 | for j := range maze[i] { 20 | fmt.Fscanf(file, "%d", &maze[i][j]) 21 | } 22 | } 23 | 24 | return maze 25 | } 26 | 27 | type point struct { 28 | i, j int 29 | } 30 | 31 | var dirs = [4]point { 32 | {-1, 0}, {0, -1}, {1, 0}, {0, 1}, 33 | } 34 | 35 | func (p point) add(r point) point { 36 | return point{p.i + r.i, p.j + r.j} 37 | } 38 | 39 | func (p point) at(grid [][]int) (int, bool) { 40 | if p.i < 0 || p.i >= len(grid) { 41 | return 0, false 42 | } 43 | 44 | if p.j < 0 || p.j >= len(grid[p.i]) { 45 | return 0, false 46 | } 47 | 48 | return grid[p.i][p.j], true 49 | } 50 | 51 | func walk(maze [][]int, start, end point) [][]int { 52 | steps := make([][] int, len(maze)) 53 | for i := range steps { 54 | steps[i] = make([]int, len(maze[i])) 55 | } 56 | 57 | Q := []point{start} 58 | 59 | for len(Q) > 0 { 60 | cur := Q[0] 61 | Q = Q[1:] 62 | 63 | if cur == end { 64 | break 65 | } 66 | 67 | for _, dir := range dirs { 68 | next := cur.add(dir) 69 | 70 | // maze at next is 0 71 | // and steps at next is 0 72 | // and next != start 73 | val, ok := next.at(maze) 74 | if !ok || val == 1 { 75 | continue 76 | } 77 | 78 | val, ok = next.at(steps) 79 | if !ok || val != 0 { 80 | continue 81 | } 82 | 83 | if next == start { 84 | continue 85 | } 86 | 87 | curSteps, _ := cur.at(steps) 88 | steps[next.i][next.j] = curSteps + 1 89 | 90 | Q = append(Q, next) 91 | } 92 | } 93 | return steps 94 | } 95 | 96 | func main() { 97 | maze := readMaze("Chapter12/maze/maze.in") 98 | 99 | //for _, row := range maze { 100 | // for _, val := range row { 101 | // fmt.Printf("%d ", val) 102 | // } 103 | // fmt.Println() 104 | //} 105 | 106 | steps := walk(maze, point{0,0}, 107 | point{len(maze)- 1, len(maze[0]) - 1}) 108 | 109 | for _, row := range steps { 110 | for _, val := range row { 111 | fmt.Printf("%3d", val) 112 | } 113 | fmt.Println() 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Chapter08/filelistingserver/errwrapper_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "os" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | func errPanic(writer http.ResponseWriter, 15 | request *http.Request) error { 16 | panic(123) 17 | } 18 | 19 | type testingUserError string 20 | 21 | func (e testingUserError) Error() string { 22 | return e.Message() 23 | } 24 | 25 | func (e testingUserError) Message() string { 26 | return string(e) 27 | } 28 | 29 | func errUserError(writer http.ResponseWriter, 30 | request *http.Request) error { 31 | return testingUserError("user error") 32 | } 33 | 34 | func errNotFound(writer http.ResponseWriter, 35 | request *http.Request) error { 36 | return os.ErrNotExist 37 | } 38 | 39 | func errNoPermission(writer http.ResponseWriter, 40 | request *http.Request) error { 41 | return os.ErrPermission 42 | } 43 | 44 | func errUnknown(writer http.ResponseWriter, 45 | request *http.Request) error { 46 | return errors.New("unknown error") 47 | } 48 | 49 | func noError(writer http.ResponseWriter, 50 | request *http.Request) error { 51 | fmt.Fprintln(writer, "no error") 52 | return nil 53 | } 54 | 55 | var tests = []struct { 56 | h appHandler 57 | code int 58 | message string 59 | }{ 60 | {errPanic, 500, "Internal Server Error"}, 61 | {errUserError, 400, "user error"}, 62 | {errNotFound, 404, "Not Found"}, 63 | {errNoPermission, 403, "Forbidden"}, 64 | {errUnknown, 500, "Internal Server Error"}, 65 | {noError, 200, "no error"}, 66 | } 67 | 68 | func TestErrWrapper(t *testing.T) { 69 | 70 | for _, tt := range tests { 71 | f := errWrapper(tt.h) 72 | response := httptest.NewRecorder() 73 | request := httptest.NewRequest( 74 | http.MethodGet, 75 | "https://www.baidu.com", nil) 76 | f(response, request) 77 | 78 | verifyResponse(response.Result(), 79 | tt.code, tt.message, t) 80 | } 81 | } 82 | 83 | func TestErrWrapperInServer(t *testing.T) { 84 | for _, tt := range tests { 85 | f := errWrapper(tt.h) 86 | server := httptest.NewServer( 87 | http.HandlerFunc(f)) 88 | resp, _ := http.Get(server.URL) 89 | 90 | verifyResponse(resp, tt.code, tt.message, t) 91 | } 92 | } 93 | 94 | func verifyResponse(resp *http.Response, 95 | expectedCode int, expectedMsg string, 96 | t *testing.T) { 97 | b, _ := ioutil.ReadAll(resp.Body) 98 | body := strings.Trim(string(b), "\n") 99 | 100 | if resp.StatusCode != expectedCode || 101 | body != expectedMsg { 102 | t.Errorf("expect (%d, %s); " + 103 | "got (%d, %s)", 104 | expectedCode, expectedMsg, 105 | resp.StatusCode, body) 106 | } 107 | } -------------------------------------------------------------------------------- /Chapter11/filelistingserver/errwrapper_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "os" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | func errPanic(writer http.ResponseWriter, 15 | request *http.Request) error { 16 | panic(123) 17 | } 18 | 19 | type testingUserError string 20 | 21 | func (e testingUserError) Error() string { 22 | return e.Message() 23 | } 24 | 25 | func (e testingUserError) Message() string { 26 | return string(e) 27 | } 28 | 29 | func errUserError(writer http.ResponseWriter, 30 | request *http.Request) error { 31 | return testingUserError("user error") 32 | } 33 | 34 | func errNotFound(writer http.ResponseWriter, 35 | request *http.Request) error { 36 | return os.ErrNotExist 37 | } 38 | 39 | func errNoPermission(writer http.ResponseWriter, 40 | request *http.Request) error { 41 | return os.ErrPermission 42 | } 43 | 44 | func errUnknown(writer http.ResponseWriter, 45 | request *http.Request) error { 46 | return errors.New("unknown error") 47 | } 48 | 49 | func noError(writer http.ResponseWriter, 50 | request *http.Request) error { 51 | fmt.Fprintln(writer, "no error") 52 | return nil 53 | } 54 | 55 | var tests = []struct { 56 | h appHandler 57 | code int 58 | message string 59 | }{ 60 | {errPanic, 500, "Internal Server Error"}, 61 | {errUserError, 400, "user error"}, 62 | {errNotFound, 404, "Not Found"}, 63 | {errNoPermission, 403, "Forbidden"}, 64 | {errUnknown, 500, "Internal Server Error"}, 65 | {noError, 200, "no error"}, 66 | } 67 | 68 | func TestErrWrapper(t *testing.T) { 69 | 70 | for _, tt := range tests { 71 | f := errWrapper(tt.h) 72 | response := httptest.NewRecorder() 73 | request := httptest.NewRequest( 74 | http.MethodGet, 75 | "https://www.baidu.com", nil) 76 | f(response, request) 77 | 78 | verifyResponse(response.Result(), 79 | tt.code, tt.message, t) 80 | } 81 | } 82 | 83 | func TestErrWrapperInServer(t *testing.T) { 84 | for _, tt := range tests { 85 | f := errWrapper(tt.h) 86 | server := httptest.NewServer( 87 | http.HandlerFunc(f)) 88 | resp, _ := http.Get(server.URL) 89 | 90 | verifyResponse(resp, tt.code, tt.message, t) 91 | } 92 | } 93 | 94 | func verifyResponse(resp *http.Response, 95 | expectedCode int, expectedMsg string, 96 | t *testing.T) { 97 | b, _ := ioutil.ReadAll(resp.Body) 98 | body := strings.Trim(string(b), "\n") 99 | 100 | if resp.StatusCode != expectedCode || 101 | body != expectedMsg { 102 | t.Errorf("expect (%d, %s); " + 103 | "got (%d, %s)", 104 | expectedCode, expectedMsg, 105 | resp.StatusCode, body) 106 | } 107 | } --------------------------------------------------------------------------------