├── .gitignore
├── 978-1-4842-8095-9.jpg
├── Contributing.md
├── LICENSE.txt
├── README.md
├── appendix-fuzzing
└── fuzzing_test.go
├── appendix-generics
├── complex_generics.go
├── generics.go
├── iterate.go
├── no_generics.go
├── simple_generics.go
└── with_generics.go
├── ch10
├── cedict_ts.u8
├── css
│ ├── cardstylesheet.css
│ └── listflashcardsstylesheet.css
├── dictionary.go
├── flashcards.go
├── flashcardsets
│ ├── common_words
│ ├── lesson_04_surname_first_name
│ ├── lesson_05_country_nationality
│ └── lesson_06_city_native_place
├── html
│ ├── listflashcards.html
│ ├── listwords.html
│ └── showflashcards.html
├── jscript
│ ├── jquery.js
│ ├── slideviewer.js
│ └── sorttable.js
├── pinyinformatter.go
└── server.go
├── ch11
├── escapestring.go
├── readhtml.go
└── sample.html
├── ch12
├── marshal.go
├── parsexml.go
├── person.xml
└── unmarshal.go
├── ch13
├── arithclient.go
├── arithserver.go
├── jsonarithclient.go
├── jsonarithserver.go
├── tcparithclient.go
└── tcparithserver.go
├── ch14
├── client.go
├── clientjson.go
├── flashcardsets
│ ├── CommonWords
│ │ ├── 你好
│ │ ├── 你好吗
│ │ ├── 再见
│ │ ├── 厕所
│ │ ├── 喂
│ │ ├── 好
│ │ └── 谢谢
│ └── Lesson04
│ │ ├── 不
│ │ ├── 也
│ │ ├── 人
│ │ ├── 什么
│ │ ├── 他
│ │ ├── 他们
│ │ ├── 你
│ │ ├── 去
│ │ ├── 叫
│ │ ├── 名字
│ │ ├── 哪
│ │ ├── 囶
│ │ ├── 国家
│ │ ├── 她
│ │ ├── 她们
│ │ ├── 姓
│ │ ├── 学生
│ │ ├── 小姐
│ │ ├── 很
│ │ ├── 恁
│ │ ├── 想
│ │ ├── 我
│ │ ├── 是
│ │ ├── 是的
│ │ ├── 朋友
│ │ ├── 没
│ │ ├── 海
│ │ ├── 美国
│ │ ├── 老师
│ │ ├── 英国
│ │ ├── 认识
│ │ ├── 谁
│ │ ├── 贵
│ │ └── 都
├── server.go
└── xml
│ ├── ListFlashcardSets.xml
│ └── ListOneFlashcardSet.xml
├── ch15
├── echoclient.go
├── echoclientgorilla.go
├── echoclienttls.go
├── echoserver.go
├── echoservergorilla.go
├── echoservertls.go
├── personclientjson.go
├── personclientxml.go
├── personserverjson.go
├── personserverxml.go
├── sensors.sh
├── temperatureserver.go
├── websocket.html
└── xmlcodec.go
├── ch16
├── contenttype.go
├── gmux.go
├── logging.go
├── matching.go
├── rpc.go
├── schema.go
└── securecookie.go
├── ch17
└── basic_http_test.go
├── ch3
├── daytimeserver.go
├── getheadinfo.go
├── ip.go
├── ipgetheadinfo.go
├── ipv4mask.go
├── ipv4router.go
├── lookuphost.go
├── lookupport.go
├── mask.go
├── ping.go
├── resolveip.go
├── simpleechoserver.go
├── threadedechoserver.go
├── threadedipechoserver.go
├── udpdaytimeclient.go
└── udpdaytimeserver.go
├── ch4
├── asn1.go
├── asn1basic.go
├── asn1fields.go
├── asn1pointers.go
├── asndaytimeclient.go
├── asndaytimeserver.go
├── badtype
│ ├── code.go
│ └── go.mod
├── base64.go
├── driver.go
├── gobechoclient.go
├── gobechoserver.go
├── jsonechoclient.go
├── jsonechoserver.go
├── loadgob.go
├── loadjson.go
├── myapp
│ ├── protocolbuffer.go
│ └── protos
│ │ └── personv3.pb.go
├── newbase64coders.go
├── personv3.proto
├── prettyjson.go
├── savegob.go
└── savejson.go
├── ch5
├── ftpclient.go
├── ftpserver.go
└── textprotoclient.go
├── ch6
├── isotounicode.go
├── runeprint.go
├── utf16client.go
├── utf16encodedecode.go
├── utf16server.go
└── utfnorm.go
├── ch7
├── aes.go
├── genrsakeys.go
├── genx509cert.go
├── hmacmd5.go
├── largekey.go
├── loadrsakeys.go
├── loadx509cert.go
├── md5hash.go
├── sha256.go
├── tlsechoclient.go
├── tlsechoserver.go
└── tlsgethead.go
├── ch8
├── clientget.go
├── fileserver.go
├── get.go
├── head.go
├── httpsfileserver.go
├── printenv.go
├── proxyauthget.go
├── proxyget.go
├── punycode.go
├── serverhandler.go
└── tlsunsafeclientget.go
├── ch9
├── printemails.go
├── printjsonemails.go
├── printnameemails.go
├── printperson.go
└── sequence.go
└── errata.md
/.gitignore:
--------------------------------------------------------------------------------
1 | **/ignore-*
2 |
--------------------------------------------------------------------------------
/978-1-4842-8095-9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/network-prog-with-go-2e/d8b0fd1f54fd2710e8e8a65ad804cbc2e7f63831/978-1-4842-8095-9.jpg
--------------------------------------------------------------------------------
/Contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing to Apress Source Code
2 |
3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers.
4 |
5 | ## How to Contribute
6 |
7 | 1. Make sure you have a GitHub account.
8 | 2. Fork the repository for the relevant book.
9 | 3. Create a new branch on which to make your change, e.g.
10 | `git checkout -b my_code_contribution`
11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted.
12 | 5. Submit a pull request.
13 |
14 | Thank you for your contribution!
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Freeware License, some rights reserved
2 |
3 | Copyright (c) 2022 Jan Newmarch and Ronald Petty
4 |
5 | Permission is hereby granted, free of charge, to anyone obtaining a copy
6 | of this software and associated documentation files (the "Software"),
7 | to work with the Software within the limits of freeware distribution and fair use.
8 | This includes the rights to use, copy, and modify the Software for personal use.
9 | Users are also allowed and encouraged to submit corrections and modifications
10 | to the Software for the benefit of other users.
11 |
12 | It is not allowed to reuse, modify, or redistribute the Software for
13 | commercial use in any way, or for a user’s educational materials such as books
14 | or blog articles without prior permission from the copyright holder.
15 |
16 | The above copyright notice and this permission notice need to be included
17 | in all copies or substantial portions of the software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | SOFTWARE.
26 |
27 |
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Apress Source Code
2 |
3 | This repository accompanies [*Network Programming with Go Language*](https://link.springer.com/book/10.1007/978-1-4842-8095-9) by Jan Newmarch and Ronald Petty (Apress, 2022).
4 |
5 | [comment]: #cover
6 | 
7 |
8 | Download the files as a zip using the green button, or clone the repository to your machine using Git.
9 |
10 | ## Releases
11 |
12 | Release v1.0 corresponds to the code in the published book, without corrections or updates.
13 |
14 | ## Contributions
15 |
16 | See the file Contributing.md for more information on how you can contribute to this repository.
--------------------------------------------------------------------------------
/appendix-fuzzing/fuzzing_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "encoding/hex"
7 | "net/http"
8 | "net/http/httptest"
9 | "testing"
10 | )
11 |
12 | func FuzzMe(f *testing.F) {
13 | for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} {
14 | f.Add(seed)
15 | }
16 |
17 | // the fuzz runner f leverages the test runner t,
18 | // this is so the fuzzer can manage the tests, it generates (or uses seed) inputs
19 | // calling the passed in test
20 | f.Fuzz(func(t *testing.T, in []byte) {
21 | enc := hex.EncodeToString(in)
22 | out, err := hex.DecodeString(enc)
23 | if err != nil {
24 | t.Fatalf("%v: decode: %v", in, err)
25 | }
26 | if !bytes.Equal(in, out) {
27 | t.Fatalf("%v: not equal after round trip: %v", in, out)
28 | }
29 | })
30 | }
31 |
32 | func FuzzBad(f *testing.F) {
33 | f.Fuzz(func(t *testing.T, i int) {
34 | if i != i {
35 | t.Fatalf("want: %v, got %v", i, i)
36 | }
37 | })
38 | }
39 |
40 | func FuzzHandler(f *testing.F) {
41 | f.Fuzz(func(t *testing.T, data string) {
42 | v := base64.StdEncoding.EncodeToString([]byte(data))
43 | req := httptest.NewRequest("GET", "/?q="+v, nil)
44 | res := httptest.NewRecorder()
45 |
46 | f := func(w http.ResponseWriter, req *http.Request) {
47 | keys, ok := req.URL.Query()["q"]
48 |
49 | if !ok || len(keys) != 1 {
50 | t.Log(keys)
51 | t.Fatal("q param missing or more than one instance")
52 | }
53 |
54 | val := keys[0]
55 |
56 | if len(val) > 16384 {
57 | w.WriteHeader(http.StatusNotAcceptable)
58 | } else {
59 | w.WriteHeader(http.StatusOK)
60 | }
61 | }
62 |
63 | f(res, req)
64 |
65 | if res == nil || res.Result().StatusCode != http.StatusOK {
66 | t.Fatal(res)
67 | }
68 | })
69 | }
70 |
--------------------------------------------------------------------------------
/appendix-generics/complex_generics.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "crypto/tls"
7 | "fmt"
8 | "io"
9 | "net/http"
10 | "strings"
11 | )
12 |
13 | type myStruct struct {
14 | s *strings.Reader
15 | }
16 |
17 | func (m myStruct) Len() int {
18 | return m.s.Len()
19 | }
20 |
21 | func (m myStruct) Read(b []byte) (int, error) {
22 | return m.s.Read(b)
23 | }
24 |
25 | type MyType interface {
26 | *bytes.Buffer | *bytes.Reader | *strings.Reader | myStruct
27 |
28 | Len() int
29 | io.Reader
30 | comparable
31 | }
32 |
33 | type Lener interface {
34 | Len() int
35 | }
36 |
37 | // ./http/httptest/httptest.go
38 | func NewRequest[M MyType](method, target string, body M) *http.Request {
39 | if method == "" {
40 | method = "GET"
41 | }
42 | req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(method + " " + target + " HTTP/1.0\r\n\r\n")))
43 | if err != nil {
44 | panic("invalid NewRequest arguments; " + err.Error())
45 | }
46 |
47 | // HTTP/1.0 was used above to avoid needing a Host field. Change it to 1.1 here.
48 | req.Proto = "HTTP/1.1"
49 | req.ProtoMinor = 1
50 | req.Close = false
51 |
52 | var zero M
53 | if body != zero {
54 | switch i := any(body).(type) {
55 | case Lener, io.ReadCloser:
56 | if b, ok := i.(Lener); ok {
57 | req.ContentLength = int64(b.Len())
58 | }
59 | if rc, ok := i.(io.ReadCloser); ok {
60 | req.Body = rc
61 | }
62 | default:
63 | req.Body = io.NopCloser(body)
64 | }
65 | } else {
66 | req.ContentLength = -1
67 | }
68 |
69 | // 192.0.2.0/24 is "TEST-NET" in RFC 5737 for use solely in
70 | // documentation and example source code and should not be
71 | // used publicly.
72 | req.RemoteAddr = "192.0.2.1:1234"
73 |
74 | if req.Host == "" {
75 | req.Host = "example.com"
76 | }
77 |
78 | if strings.HasPrefix(target, "https://") {
79 | req.TLS = &tls.ConnectionState{
80 | Version: tls.VersionTLS12,
81 | HandshakeComplete: true,
82 | ServerName: req.Host,
83 | }
84 | }
85 |
86 | return req
87 | }
88 |
89 | func main() {
90 | fmt.Println(NewRequest("GET", "/", myStruct{strings.NewReader("")}).ContentLength)
91 | fmt.Println(NewRequest("GET", "/", myStruct{}).ContentLength)
92 | fmt.Println(NewRequest("GET", "/", strings.NewReader("")).ContentLength)
93 | fmt.Println(NewRequest("GET", "/", &bytes.Buffer{}).ContentLength)
94 | fmt.Println(NewRequest("GET", "/", bytes.NewReader([]byte("read me"))).ContentLength)
95 | }
96 |
--------------------------------------------------------------------------------
/appendix-generics/generics.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "crypto/tls"
7 | "fmt"
8 | "io"
9 | "net/http"
10 | "strings"
11 | )
12 |
13 | type Animal interface {
14 | Sound()
15 | }
16 |
17 | type Cat struct{}
18 |
19 | func (c Cat) Sound() { fmt.Println("Meow") }
20 |
21 | type Dog struct{}
22 |
23 | func (d Dog) Sound() { fmt.Println("Woof") }
24 |
25 | type Hybrid interface {
26 | Animal
27 | }
28 |
29 | func test[H Hybrid](which H) H {
30 | which.Sound()
31 | return which
32 | /*
33 | switch any(which).(type) {
34 | case Dog:
35 | which.Sound()
36 | return which
37 | case Cat:
38 | which.Sound()
39 | return which
40 | default:
41 | which.Sound()
42 | return which
43 | }
44 | */
45 | }
46 |
47 | type myStruct struct {
48 | s *strings.Reader
49 | }
50 |
51 | func (m myStruct) Len() int {
52 | return m.s.Len()
53 | }
54 |
55 | func (m myStruct) Read(b []byte) (int, error) {
56 | return m.s.Read(b)
57 | }
58 |
59 | func main() {
60 | c := test(Cat{})
61 | d := test(Dog{})
62 |
63 | c = c
64 | d = d
65 |
66 | r1 := NewRequest("GET", "/", myStruct{strings.NewReader("")})
67 | r2 := NewRequest("GET", "/", myStruct{})
68 |
69 | r1 = r1
70 | r2 = r2
71 | }
72 |
73 | type MyType interface {
74 | bytes.Buffer | bytes.Reader | strings.Reader | myStruct
75 |
76 | Len() int
77 | io.Reader
78 | comparable
79 | }
80 |
81 | // ./http/httptest/httptest.go
82 | func NewRequest[M MyType](method, target string, body M) *http.Request {
83 | if method == "" {
84 | method = "GET"
85 | }
86 | req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(method + " " + target + " HTTP/1.0\r\n\r\n")))
87 | if err != nil {
88 | panic("invalid NewRequest arguments; " + err.Error())
89 | }
90 |
91 | // HTTP/1.0 was used above to avoid needing a Host field. Change it to 1.1 here.
92 | req.Proto = "HTTP/1.1"
93 | req.ProtoMinor = 1
94 | req.Close = false
95 |
96 | var zero M
97 | if body != zero {
98 | req.ContentLength = int64(body.Len())
99 | switch v := any(body).(type) {
100 | case io.ReadCloser:
101 | req.Body = v
102 | default:
103 | req.Body = io.NopCloser(body)
104 | }
105 | } else {
106 | req.ContentLength = -1
107 | }
108 |
109 | // 192.0.2.0/24 is "TEST-NET" in RFC 5737 for use solely in
110 | // documentation and example source code and should not be
111 | // used publicly.
112 | req.RemoteAddr = "192.0.2.1:1234"
113 |
114 | if req.Host == "" {
115 | req.Host = "example.com"
116 | }
117 |
118 | if strings.HasPrefix(target, "https://") {
119 | req.TLS = &tls.ConnectionState{
120 | Version: tls.VersionTLS12,
121 | HandshakeComplete: true,
122 | ServerName: req.Host,
123 | }
124 | }
125 |
126 | return req
127 | }
128 |
--------------------------------------------------------------------------------
/appendix-generics/iterate.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | "time"
7 | )
8 |
9 | // Our LinkedList code
10 | type LL struct {
11 | N *LL
12 | data string
13 | }
14 |
15 | // Retreive the next node in the linkedlist
16 | func (l LL) Next() *LL { return l.N }
17 |
18 | // Here we use a union of types “|”
19 | // Meaning our arguments must be of these types
20 | // Either channel or the above linkedlist type
21 | type MustBe interface {
22 | chan string | LL
23 | }
24 |
25 | // We use the union technique once more on the return types
26 | // Notice these can differ than the above "MustBe" types
27 | type Result interface {
28 | string | LL
29 | }
30 |
31 | // This is the function we wish to make generic
32 | // We are iterating over a instance of MustBe (either channel string or LL)
33 | // Notice the return type must be a Result type (either string or LL)
34 | func Iterate[M MustBe, R Result](o M, iter func(M) R) (r R) {
35 | return iter(o)
36 | }
37 |
38 | func main() {
39 | // Create channel for strings
40 | c := make(chan string, 5)
41 | c <- "ok"
42 | c <- "ok2"
43 |
44 | //This function is what we will pass into the above Iterate
45 | //Notice Iterate's first parameter is the same as the following
46 | //lambdas parameter
47 | citer := func(c chan string) string {
48 | select {
49 | case msg1 := <-c:
50 | return msg1
51 | case <-time.After(1 * time.Second):
52 | return "nothing"
53 | }
54 | }
55 |
56 | // Here we "Iterate" through the channel
57 | var wg sync.WaitGroup
58 | wg.Add(2)
59 | go func(f func(chan string) string) {
60 | for {
61 | fmt.Println(Iterate(c, f))
62 | wg.Done()
63 | }
64 | }(citer)
65 | wg.Wait() // wait for iteration to finish
66 |
67 | // The remaining example shows passing a custom Linked List
68 | // iteration function
69 |
70 | // First we build a simple list
71 | n1 := LL{data: "n1"}
72 | n2 := LL{data: "n2"}
73 | n3 := LL{data: "n3"}
74 | n1.N = &n2
75 | n2.N = &n3
76 |
77 | // Like the above citer, the parameter type will match
78 | // the first parameter of Iterate above
79 | liter := func(l LL) LL {
80 | var zero LL
81 |
82 | if l.N != zero.N {
83 | return *l.N
84 | } else {
85 | return zero
86 | }
87 | }
88 |
89 | // We walk through the linked list
90 | n := n1
91 | for n.N != nil {
92 | fmt.Printf("node:%s\n",n.data)
93 | n = Iterate(n, liter)
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/appendix-generics/no_generics.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func FilterInt(s []int, f func(int) bool) []int {
8 | var r []int
9 | for _, v := range s {
10 | if f(v) {
11 | r = append(r, v)
12 | }
13 | }
14 |
15 | return r
16 | }
17 |
18 | func FilterString(s []string, f func(string) bool) []string {
19 | var r []string
20 | for _, v := range s {
21 | if f(v) {
22 | r = append(r, v)
23 | }
24 | }
25 |
26 | return r
27 | }
28 |
29 | func main() {
30 | evens := FilterInt([]int{1, 2, 3, 4, 5}, func(i int) bool { return i%2 == 0 })
31 | fmt.Printf("%v\n", evens)
32 |
33 | shortStrings := FilterString([]string{"ok", "notok", "maybe", "maybe not"}, func(s string) bool { return len(s) < 3 })
34 | fmt.Printf("%v\n", shortStrings)
35 | }
36 |
--------------------------------------------------------------------------------
/appendix-generics/simple_generics.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | type Animal interface {
8 | Sound()
9 | }
10 |
11 | type Cat struct{}
12 |
13 | func (c Cat) Sound() { fmt.Println("Meow") }
14 |
15 | func (c Cat) SpecialToCat() { fmt.Println("Cat special") }
16 |
17 | type Dog struct{}
18 |
19 | func (d Dog) Sound() { fmt.Println("Woof") }
20 |
21 | func (c Dog) UniqueToDog() { fmt.Println("Dog unique") }
22 |
23 | type Domesticated interface {
24 | Cat | Dog // Not Owls
25 | Animal
26 | }
27 |
28 | // Here we limit ourselves to domesticated animals
29 | // If you passed in a 'wild' animal, it would not work
30 | func SoundOff[H Domesticated](animal H) H {
31 | animal.Sound()
32 |
33 | switch a := any(animal).(type) {
34 | case Dog:
35 | a.UniqueToDog()
36 | case Cat:
37 | a.SpecialToCat()
38 | default:
39 | fmt.Println("Then hoo?")
40 | }
41 | return animal
42 | }
43 |
44 | // An Owl is an animal,
45 | // They are wild to us!
46 | type Owl struct{}
47 |
48 | func (Owl) Sound() {}
49 |
50 | func main() {
51 | var c Cat = SoundOff(Cat{})
52 | d := SoundOff(Dog{})
53 |
54 | c.Sound()
55 | c.SpecialToCat()
56 | d.Sound()
57 | d.UniqueToDog()
58 |
59 | SoundOff(Owl{})
60 | }
61 |
--------------------------------------------------------------------------------
/appendix-generics/with_generics.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func Filter[T any](s []T, f func(T) bool) []T {
8 | var r []T
9 | for _, v := range s {
10 | if f(v) {
11 | r = append(r, v)
12 | }
13 | }
14 |
15 | return r
16 | }
17 |
18 | func main() {
19 | evens := Filter([]int{1, 2, 3, 4, 5}, func(i int) bool { return i%2 == 0 })
20 | fmt.Printf("%v\n", evens)
21 |
22 | shortStrings := Filter([]string{"ok", "notok", "maybe", "maybe not"}, func(s string) bool { return len(s) < 3 })
23 | fmt.Printf("%v\n", shortStrings)
24 | }
25 |
--------------------------------------------------------------------------------
/ch10/css/cardstylesheet.css:
--------------------------------------------------------------------------------
1 | .english {
2 | background-color: rgb(126, 126, 126);
3 | padding: 5px;
4 | }
5 |
6 | .translations {
7 | background-color: rgb(216, 206, 206);
8 | padding: 5px;
9 | }
10 |
11 | .pinyin {
12 | background-color: rgb(199, 201, 201);
13 | padding: 5px;
14 | }
15 |
16 | .simplified {
17 | background-color: rgb(143, 143, 143);
18 | padding: 5px;
19 | }
20 |
21 | .traditional {
22 | background-color: rgb(182, 179, 179);
23 | padding: 5px;
24 | }
25 |
26 | body {
27 | background-color: lightgrey;
28 | }
29 |
--------------------------------------------------------------------------------
/ch10/css/listflashcardsstylesheet.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: lightgrey;
3 | }
4 |
5 | table {
6 | background-color: lightslategray;
7 | border: none;
8 | }
9 |
10 | td {
11 | padding: 10px;
12 | }
--------------------------------------------------------------------------------
/ch10/dictionary.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "fmt"
6 | "os"
7 | "strings"
8 | )
9 |
10 | /*
11 | // go run dictionary.go - will load cedict_ts.u8 and render
12 | func main() {
13 | // called only when debugging this package
14 | dictionaryPath := "cedict_ts.u8"
15 | d := new(Dictionary)
16 | d.loadDictionary(dictionaryPath)
17 |
18 | // check it loaded okay
19 | goodD := d.LookupEnglish("good")
20 | fmt.Println(goodD.String())
21 | }
22 | */
23 |
24 | type DictionaryEntry struct {
25 | Traditional string
26 | Simplified string
27 | Pinyin string
28 | Translations []string
29 | }
30 |
31 | func (de DictionaryEntry) String() string {
32 | str := de.Traditional + de.Simplified + de.Pinyin
33 | for _, t := range de.Translations {
34 | str = str + "\n " + t
35 | }
36 | return str
37 | }
38 |
39 | type Dictionary struct {
40 | Entries []*DictionaryEntry
41 | }
42 |
43 | func Init(inital_len int) *Dictionary {
44 | d := new(Dictionary)
45 | d.Entries = make([]*DictionaryEntry, inital_len)
46 | return d
47 | }
48 |
49 | func (d *Dictionary) Len() int {
50 | return len(d.Entries)
51 | }
52 |
53 | func (d *Dictionary) At(i int) *DictionaryEntry {
54 | return d.Entries[i]
55 | }
56 |
57 | func (d *Dictionary) Set(i int, x *DictionaryEntry) {
58 | d.Entries[i] = x
59 | }
60 |
61 | func (d *Dictionary) Push(x *DictionaryEntry) {
62 | d.Entries = append(d.Entries, x)
63 | }
64 |
65 | func (d *Dictionary) String() string {
66 | str := ""
67 | for n := 0; n < d.Len(); n++ {
68 | de := d.At(n)
69 | str += de.String() + "\n"
70 | }
71 | return str
72 | }
73 |
74 | func (d *Dictionary) LookupPinyin(py string) *Dictionary {
75 | newD := new(Dictionary)
76 | for n := 0; n < d.Len(); n++ {
77 | de := d.At(n)
78 | if de.Pinyin == py {
79 | newD.Push(de)
80 | }
81 | }
82 | return newD
83 | }
84 |
85 | func (d *Dictionary) LookupEnglish(eng string) *Dictionary {
86 | newD := new(Dictionary)
87 | for n := 0; n < d.Len(); n++ {
88 | de := d.At(n)
89 | for _, e := range de.Translations {
90 | if e == eng {
91 | newD.Push(de)
92 | }
93 | }
94 | }
95 | return newD
96 | }
97 |
98 | func (d *Dictionary) loadDictionary(path string) {
99 |
100 | f, err := os.Open(path)
101 | r := bufio.NewReader(f)
102 | if err != nil {
103 | fmt.Println(err)
104 | os.Exit(1)
105 | }
106 | for {
107 | line, err := r.ReadString('\n')
108 | if line[0] == '#' {
109 | continue
110 | }
111 | if err != nil {
112 | return
113 | }
114 |
115 | trad, simp, pinyin, translations := parseDictEntry(line)
116 |
117 | de := DictionaryEntry{
118 | Traditional: trad,
119 | Simplified: simp,
120 | Pinyin: pinyin,
121 | Translations: translations}
122 |
123 | d.Push(&de)
124 | }
125 | }
126 |
127 | func parseDictEntry(line string) (string, string, string, []string) {
128 | // format is
129 | // trad simp [pinyin] /trans/trans/.../
130 | tradEnd := strings.Index(line, " ")
131 | trad := line[0:tradEnd]
132 | line = strings.TrimSpace(line[tradEnd:])
133 |
134 | simpEnd := strings.Index(line, " ")
135 | simp := line[0:simpEnd]
136 | line = strings.TrimSpace(line[simpEnd:])
137 |
138 | pinyinEnd := strings.Index(line, "]")
139 | pinyin := line[1:pinyinEnd]
140 | line = strings.TrimSpace(line[pinyinEnd+1:])
141 |
142 | translations := strings.Split(line, "/")
143 | // includes empty at start and end, so
144 | translations = translations[1 : len(translations)-1]
145 |
146 | return trad, simp, pinyin, translations
147 | }
148 |
--------------------------------------------------------------------------------
/ch10/flashcards.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | // "fmt"
5 | "encoding/json"
6 | "io"
7 | "os"
8 | "sort"
9 | )
10 |
11 | type FlashCard struct {
12 | Simplified string
13 | English string
14 | Dictionary *Dictionary
15 | }
16 |
17 | type FlashCards struct {
18 | Name string
19 | CardOrder string
20 | ShowHalf string
21 | Cards []*FlashCard
22 | }
23 |
24 | func LoadJSON(r io.Reader, key any) {
25 | decoder := json.NewDecoder(r)
26 | err := decoder.Decode(key)
27 | checkError(err)
28 | }
29 |
30 | func ListFlashCardsNames() []string {
31 | flashCardsDir, err := os.Open("flashcardsets")
32 | if err != nil {
33 | return nil
34 | }
35 | files, err := flashCardsDir.Readdir(-1)
36 |
37 | fileNames := make([]string, len(files))
38 | for n, f := range files {
39 | fileNames[n] = f.Name()
40 | }
41 | sort.Strings(fileNames)
42 | return fileNames
43 | }
44 |
--------------------------------------------------------------------------------
/ch10/flashcardsets/common_words:
--------------------------------------------------------------------------------
1 | {"Name":"Common Words","CardOrder":"","ShowHalf":"","Cards":[{"Simplified":"你好","English":"hello","Dictionary":{"Entries":[{"Traditional":"你好","Simplified":"你好","Pinyin":"ni3 hao3","Translations":["hello","hi","how are you?"]}]}},{"Simplified":"喂","English":"hello (interj., esp. on telephone)","Dictionary":{"Entries":[{"Traditional":"喂","Simplified":"喂","Pinyin":"wei4","Translations":["hello (interj., esp. on telephone)","hey","to feed (sb or some animal)"]}]}},{"Simplified":"好","English":"good","Dictionary":{"Entries":[{"Traditional":"好","Simplified":"好","Pinyin":"hao3","Translations":["good","well","proper","good to","easy to","very","so","(suffix indicating completion or readiness)"]}]}},{"Simplified":"再见","English":"goodbye","Dictionary":{"Entries":[{"Traditional":"再見","Simplified":"再见","Pinyin":"zai4 jian4","Translations":["goodbye","see you again later"]}]}},{"Simplified":"你好吗","English":"How are you?","Dictionary":{"Entries":[{"Traditional":"你好嗎","Simplified":"你好吗","Pinyin":"ni3 hao3 ma5","Translations":["How are you?","Are you well?"]}]}},{"Simplified":"厕所","English":"toilet","Dictionary":{"Entries":[{"Traditional":"廁所","Simplified":"厕所","Pinyin":"ce4 suo3","Translations":["toilet","lavatory","CL:間|间[jian1],處|处[chu4]"]}]}},{"Simplified":"谢谢","English":"thanks","Dictionary":{"Entries":[{"Traditional":"謝謝","Simplified":"谢谢","Pinyin":"xie4 xie5","Translations":["to thank","thanks"]}]}}]}
2 |
--------------------------------------------------------------------------------
/ch10/flashcardsets/lesson_04_surname_first_name:
--------------------------------------------------------------------------------
1 | {"Name":"Lesson 04: Surname, First Name","CardOrder":"","ShowHalf":"","Cards":[{"Simplified":"恁","English":"you (polite)","Dictionary":{"Entries":[{"Traditional":"恁","Simplified":"恁","Pinyin":"nin2","Translations":["you (polite)"]}]}},{"Simplified":"贵","English":"your (name)","Dictionary":{"Entries":[{"Traditional":"貴","Simplified":"贵","Pinyin":"gui4","Translations":["expensive","noble","your (name)","precious"]}]}},{"Simplified":"姓","English":"surname","Dictionary":{"Entries":[{"Traditional":"姓","Simplified":"姓","Pinyin":"xing4","Translations":["family name","surname","name","CL:個|个[ge4]"]}]}},{"Simplified":"我","English":"I","Dictionary":{"Entries":[{"Traditional":"我","Simplified":"我","Pinyin":"wo3","Translations":["I","me","my"]}]}},{"Simplified":"你","English":"you (informal, as opposed to courteous 您[nin2])","Dictionary":{"Entries":[{"Traditional":"你","Simplified":"你","Pinyin":"ni3","Translations":["you (informal, as opposed to courteous 您[nin2])"]}]}},{"Simplified":"叫","English":"to be called","Dictionary":{"Entries":[{"Traditional":"叫","Simplified":"叫","Pinyin":"jiao4","Translations":["to shout","to call","to order","to ask","to be called","by (indicates agent in the passive mood)"]}]}},{"Simplified":"什么","English":"what?","Dictionary":{"Entries":[{"Traditional":"什麼","Simplified":"什么","Pinyin":"shen2 me5","Translations":["what?","who?","something","anything"]}]}},{"Simplified":"名字","English":"name (of a person or thing)","Dictionary":{"Entries":[{"Traditional":"名字","Simplified":"名字","Pinyin":"ming2 zi5","Translations":["name (of a person or thing)","CL:個|个[ge4]"]}]}},{"Simplified":"他","English":"he or him","Dictionary":{"Entries":[{"Traditional":"他","Simplified":"他","Pinyin":"ta1","Translations":["he or him","(used for either sex when the sex is unknown or unimportant)","(used before sb's name for emphasis)","(used as a meaningless mock object)","other","another"]}]}},{"Simplified":"她","English":"she","Dictionary":{"Entries":[{"Traditional":"她","Simplified":"她","Pinyin":"ta1","Translations":["she"]}]}},{"Simplified":"是","English":"is","Dictionary":{"Entries":[{"Traditional":"是","Simplified":"是","Pinyin":"shi4","Translations":["is","are","am","yes","to be"]}]}},{"Simplified":"谁","English":"who","Dictionary":{"Entries":[{"Traditional":"誰","Simplified":"谁","Pinyin":"shei2","Translations":["who","also pronounced shui2"]}]}},{"Simplified":"老师","English":"teacher","Dictionary":{"Entries":[{"Traditional":"老師","Simplified":"老师","Pinyin":"lao3 shi1","Translations":["teacher","CL:個|个[ge4],位[wei4]"]}]}},{"Simplified":"认识","English":"to know","Dictionary":{"Entries":[{"Traditional":"認識","Simplified":"认识","Pinyin":"ren4 shi5","Translations":["to know","to recognize","to be familiar with","acquainted with sth","knowledge","understanding","awareness","cognition"]}]}},{"Simplified":"学生","English":"student","Dictionary":{"Entries":[{"Traditional":"學生","Simplified":"学生","Pinyin":"xue2 sheng5","Translations":["student","school child"]}]}},{"Simplified":"朋友","English":"friend","Dictionary":{"Entries":[{"Traditional":"朋友","Simplified":"朋友","Pinyin":"peng2 you5","Translations":["friend","CL:個|个[ge4],位[wei4]"]}]}},{"Simplified":"海","English":"sea","Dictionary":{"Entries":[{"Traditional":"海","Simplified":"海","Pinyin":"hai3","Translations":["ocean","sea","CL:個|个[ge4],片[pian4]"]}]}},{"Simplified":"去","English":"to go","Dictionary":{"Entries":[{"Traditional":"去","Simplified":"去","Pinyin":"qu4","Translations":["to go","to go to (a place)","to cause to go or send (sb)","to remove","to get rid of","(when used either before or after a verb) to go in order to do sth","to be apart from in space or time","(after a verb of motion indicates movement away from the speaker)","(used after certain verbs to indicate detachment or separation)","(of a time or an event etc) just passed or elapsed"]}]}},{"Simplified":"哪","English":"which","Dictionary":{"Entries":[{"Traditional":"哪","Simplified":"哪","Pinyin":"na3","Translations":["how","which"]}]}},{"Simplified":"国家","English":"country","Dictionary":{"Entries":[{"Traditional":"國家","Simplified":"国家","Pinyin":"guo2 jia1","Translations":["country","nation","state","CL:個|个[ge4]"]}]}},{"Simplified":"人","English":"person","Dictionary":{"Entries":[{"Traditional":"人","Simplified":"人","Pinyin":"ren2","Translations":["man","person","people","CL:個|个[ge4],位[wei4]"]}]}},{"Simplified":"是的","English":"yes","Dictionary":{"Entries":[{"Traditional":"是的","Simplified":"是的","Pinyin":"shi4 de5","Translations":["yes"]}]}},{"Simplified":"小姐","English":"miss","Dictionary":{"Entries":[{"Traditional":"小姐","Simplified":"小姐","Pinyin":"xiao3 jie5","Translations":["young lady","miss","CL:個|个[ge4],位[wei4]"]}]}},{"Simplified":"囶","English":"country","Dictionary":{"Entries":[{"Traditional":"囶","Simplified":"囶","Pinyin":"guo2","Translations":["variant of 國|国","country"]}]}},{"Simplified":"美国","English":"USA","Dictionary":{"Entries":[{"Traditional":"美國","Simplified":"美国","Pinyin":"Mei3 guo2","Translations":["United States","USA","US"]}]}},{"Simplified":"他们","English":"they","Dictionary":{"Entries":[{"Traditional":"他們","Simplified":"他们","Pinyin":"ta1 men5","Translations":["they"]}]}},{"Simplified":"她们","English":"they","Dictionary":{"Entries":[{"Traditional":"她們","Simplified":"她们","Pinyin":"ta1 men5","Translations":["they","them (for females)"]}]}},{"Simplified":"也","English":"also","Dictionary":{"Entries":[{"Traditional":"也","Simplified":"也","Pinyin":"ye3","Translations":["also","too","(in Classical Chinese) final particle implying affirmation"]}]}},{"Simplified":"不","English":"no","Dictionary":{"Entries":[{"Traditional":"不","Simplified":"不","Pinyin":"bu4","Translations":["(negative prefix)","not","no"]}]}},{"Simplified":"都","English":"all, both","Dictionary":{"Entries":[{"Traditional":"都","Simplified":"都","Pinyin":"dou1","Translations":["all, both","entirely (due to) each","even","already"]}]}},{"Simplified":"英国","English":"United Kingdom of Great Britain and Northern Ireland","Dictionary":{"Entries":[{"Traditional":"英國","Simplified":"英国","Pinyin":"Ying1 guo2","Translations":["United Kingdom 聯合王國|联合王国[Lian2 he2 wang2 guo2]","United Kingdom of Great Britain and Northern Ireland","abbr. for England 英格蘭|英格兰[Ying1 ge2 lan2]"]}]}},{"Simplified":"没","English":"(negative prefix for verbs)","Dictionary":{"Entries":[{"Traditional":"沒","Simplified":"没","Pinyin":"mei2","Translations":["(negative prefix for verbs)","have not","not"]}]}},{"Simplified":"想","English":"to think","Dictionary":{"Entries":[{"Traditional":"想","Simplified":"想","Pinyin":"xiang3","Translations":["to think","to believe","to suppose","to wish","to want","to miss"]}]}},{"Simplified":"很","English":"very","Dictionary":{"Entries":[{"Traditional":"很","Simplified":"很","Pinyin":"hen3","Translations":["(adverb of degree)","quite","very","awfully"]}]}}]}
2 |
--------------------------------------------------------------------------------
/ch10/flashcardsets/lesson_05_country_nationality:
--------------------------------------------------------------------------------
1 | {"Name":"Lesson 05: Country, Nationality","CardOrder":"","ShowHalf":"","Cards":[{"Simplified":"去","English":"to go","Dictionary":{"Entries":[{"Traditional":"去","Simplified":"去","Pinyin":"qu4","Translations":["to go","to go to (a place)","to cause to go or send (sb)","to remove","to get rid of","(when used either before or after a verb) to go in order to do sth","to be apart from in space or time","(after a verb of motion indicates movement away from the speaker)","(used after certain verbs to indicate detachment or separation)","(of a time or an event etc) just passed or elapsed"]}]}},{"Simplified":"哪","English":"which","Dictionary":{"Entries":[{"Traditional":"哪","Simplified":"哪","Pinyin":"na3","Translations":["how","which"]}]}},{"Simplified":"个","English":"classifier for people or objects in general","Dictionary":{"Entries":[{"Traditional":"個","Simplified":"个","Pinyin":"ge4","Translations":["individual","this","that","size","classifier for people or objects in general"]}]}},{"Simplified":"国家","English":"country","Dictionary":{"Entries":[{"Traditional":"國家","Simplified":"国家","Pinyin":"guo2 jia1","Translations":["country","nation","state","CL:個|个[ge4]"]}]}},{"Simplified":"中国","English":"China","Dictionary":{"Entries":[{"Traditional":"中國","Simplified":"中国","Pinyin":"Zhong1 guo2","Translations":["China","Middle Kingdom"]}]}},{"Simplified":"人","English":"person","Dictionary":{"Entries":[{"Traditional":"人","Simplified":"人","Pinyin":"ren2","Translations":["man","person","people","CL:個|个[ge4],位[wei4]"]}]}},{"Simplified":"吗","English":"(question tag)","Dictionary":{"Entries":[{"Traditional":"嗎","Simplified":"吗","Pinyin":"ma5","Translations":["(question tag)"]}]}},{"Simplified":"是的","English":"yes","Dictionary":{"Entries":[{"Traditional":"是的","Simplified":"是的","Pinyin":"shi4 de5","Translations":["yes"]}]}},{"Simplified":"国","English":"country","Dictionary":{"Entries":[{"Traditional":"國","Simplified":"国","Pinyin":"guo2","Translations":["country","nation","state","national","CL:個|个[ge4]"]}]}},{"Simplified":"美国","English":"USA","Dictionary":{"Entries":[{"Traditional":"美國","Simplified":"美国","Pinyin":"Mei3 guo2","Translations":["United States","USA","US"]}]}},{"Simplified":"们","English":"plural marker for pronouns, and nouns referring to individuals","Dictionary":{"Entries":[{"Traditional":"們","Simplified":"们","Pinyin":"men5","Translations":["plural marker for pronouns, and nouns referring to individuals"]}]}},{"Simplified":"他们","English":"they","Dictionary":{"Entries":[{"Traditional":"他們","Simplified":"他们","Pinyin":"ta1 men5","Translations":["they"]}]}},{"Simplified":"也","English":"also","Dictionary":{"Entries":[{"Traditional":"也","Simplified":"也","Pinyin":"ye3","Translations":["also","too","(in Classical Chinese) final particle implying affirmation"]}]}},{"Simplified":"不","English":"no","Dictionary":{"Entries":[{"Traditional":"不","Simplified":"不","Pinyin":"bu4","Translations":["(negative prefix)","not","no"]}]}},{"Simplified":"都","English":"all, both","Dictionary":{"Entries":[{"Traditional":"都","Simplified":"都","Pinyin":"dou1","Translations":["all, both","entirely (due to) each","even","already"]}]}},{"Simplified":"英国","English":"United Kingdom 聯合王國|联合王国[Lian2 he2 wang2 guo2]","Dictionary":{"Entries":[{"Traditional":"英國","Simplified":"英国","Pinyin":"Ying1 guo2","Translations":["United Kingdom 聯合王國|联合王国[Lian2 he2 wang2 guo2]","United Kingdom of Great Britain and Northern Ireland","abbr. for England 英格蘭|英格兰[Ying1 ge2 lan2]"]}]}},{"Simplified":"想","English":"to want","Dictionary":{"Entries":[{"Traditional":"想","Simplified":"想","Pinyin":"xiang3","Translations":["to think","to believe","to suppose","to wish","to want","to miss"]}]}},{"Simplified":"很","English":"very","Dictionary":{"Entries":[{"Traditional":"很","Simplified":"很","Pinyin":"hen3","Translations":["(adverb of degree)","quite","very","awfully"]}]}}]}
2 |
--------------------------------------------------------------------------------
/ch10/flashcardsets/lesson_06_city_native_place:
--------------------------------------------------------------------------------
1 | {"Name":"Lesson 06: City, Native Place","CardOrder":"","ShowHalf":"","Cards":[{"Simplified":"这","English":"this","Dictionary":{"Entries":[{"Traditional":"這","Simplified":"这","Pinyin":"zhe4","Translations":["this","these","(commonly pr. zhei4 before a classifier, esp. in Beijing)"]}]}},{"Simplified":"那","English":"that","Dictionary":{"Entries":[{"Traditional":"那","Simplified":"那","Pinyin":"na4","Translations":["that","those","then (in that case)","commonly pr. nei4 before a classifier, esp. in Beijing"]}]}},{"Simplified":"国家","English":"country","Dictionary":{"Entries":[{"Traditional":"國家","Simplified":"国家","Pinyin":"guo2 jia1","Translations":["country","nation","state","CL:個|个[ge4]"]}]}},{"Simplified":"地方","English":"place","Dictionary":{"Entries":[{"Traditional":"地方","Simplified":"地方","Pinyin":"di4 fang5","Translations":["area","place","space","room","territory","CL:處|处[chu4],個|个[ge4],塊|块[kuai4]"]}]}},{"Simplified":"去","English":"to go","Dictionary":{"Entries":[{"Traditional":"去","Simplified":"去","Pinyin":"qu4","Translations":["to go","to go to (a place)","to cause to go or send (sb)","to remove","to get rid of","(when used either before or after a verb) to go in order to do sth","to be apart from in space or time","(after a verb of motion indicates movement away from the speaker)","(used after certain verbs to indicate detachment or separation)","(of a time or an event etc) just passed or elapsed"]}]}},{"Simplified":"哪","English":"which","Dictionary":{"Entries":[{"Traditional":"哪","Simplified":"哪","Pinyin":"na3","Translations":["how","which"]}]}},{"Simplified":"大","English":"big","Dictionary":{"Entries":[{"Traditional":"大","Simplified":"大","Pinyin":"da4","Translations":["big","huge","large","major","great","wide","deep","oldest","eldest"]}]}},{"Simplified":"小","English":"small","Dictionary":{"Entries":[{"Traditional":"小","Simplified":"小","Pinyin":"xiao3","Translations":["small","tiny","few","young"]}]}},{"Simplified":"可是","English":"but","Dictionary":{"Entries":[{"Traditional":"可是","Simplified":"可是","Pinyin":"ke3 shi4","Translations":["but","however"]}]}},{"Simplified":"多","English":"many","Dictionary":{"Entries":[{"Traditional":"多","Simplified":"多","Pinyin":"duo1","Translations":["many","much","a lot of","numerous","multi-"]}]}}]}
2 |
--------------------------------------------------------------------------------
/ch10/html/listflashcards.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flashcards
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Flashcards
16 |
17 |
18 |
19 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ch10/html/listwords.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Words for {{.Name}}
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Words for {{.Name}}
19 |
20 |
21 |
22 |
23 | English |
24 | Pinyin |
25 | Traditional |
26 | Simplified |
27 |
28 | {{range .Cards}}
29 |
30 |
31 |
32 |
33 |
34 | {{.English}}
35 | |
36 |
37 |
38 |
39 | {{with .Dictionary}}
40 | {{range .Entries}}
41 |
42 |
43 |
44 | {{.Pinyin|pinyin}}
45 | |
46 |
47 |
48 |
49 |
50 |
51 |
52 | {{.Traditional}}
53 | |
54 |
55 |
56 |
57 |
58 |
59 |
60 | {{.Simplified}}
61 | |
62 |
63 |
64 |
65 | {{end}}
66 | {{end}}
67 |
68 |
69 | {{end}}
70 |
71 |
72 |
73 | Return to Flash Cards list
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/ch10/html/showflashcards.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flashcards for {{.Name}}
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
28 | Flashcards for {{.Name}}
29 |
30 |
31 |
32 | {{range .Cards}}
33 |
34 |
35 |
36 | English: {{.English}}
37 |
38 |
39 |
40 | {{with .Dictionary}}
41 | {{range .Entries}}
42 |
43 |
44 | Pinyin: {{.Pinyin|pinyin}}
45 |
46 |
47 |
48 |
49 |
50 | Traditional: {{.Traditional}}
51 |
52 |
53 |
54 |
55 |
56 | Simplified: {{.Simplified}}
57 |
58 |
59 |
60 |
61 |
62 | Translations:
63 | {{range .Translations}}
64 | {{.}}
65 | {{end}}
66 |
67 |
68 | {{end}}
69 | {{end}}
70 |
71 | {{end}}
72 |
73 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/ch10/jscript/slideviewer.js:
--------------------------------------------------------------------------------
1 | // var touchy=('ontouchstart' in document.documentElement)?true:false;`
2 | var RANDOM = 1;
3 | var SEQUENTIAL = 2;
4 | var ENGLISH_HALF = 3;
5 | var CHINESE_HALF = 4;
6 | var RANDOM_HALF = 5;
7 |
8 | var SHOW_HALF = 1;
9 | var SHOW_FULL = 2;
10 |
11 | // overridden by form fields
12 | var cardOrder = SEQUENTIAL;
13 | var showHalfCard = RANDOM_HALF;
14 |
15 | var cardState = SHOW_HALF;
16 |
17 | var currentCard;
18 | var cards;
19 |
20 | // window.onkeypress = keypress;
21 | //document.onkeypress = keypress;
22 |
23 | function initSlides() {
24 | cards = new Cards(cardOrder, showHalfCard);
25 | cardStr = new String("card")
26 |
27 | var allDivElmts = document.getElementsByTagName("div");
28 | var j = 0;
29 | for (var i = 0; i < allDivElmts.length; i++) {
30 |
31 | if (allDivElmts[i].className == "card") {
32 | cards.push(allDivElmts[i]);
33 | }
34 | }
35 | }
36 |
37 | function showSlides() {
38 |
39 | initSlides();
40 |
41 | allVisible = false;
42 |
43 | currentCard = cards.getNextCard();
44 | currentCard.showHalf();
45 | }
46 |
47 | /*
48 | function keypress(evt) {
49 | // FF var keychar = (evt.keycode) ? evt.keycode : evt.which;
50 | //var keychar = evt.charCode || evt.keyCode;
51 | keychar = evt.which;
52 | alert(keychar);
53 | if (keychar != 32) {
54 | return true;
55 | }
56 |
57 | if (cardState == SHOW_HALF) {
58 | currentCard.show();
59 | cardState = SHOW_FULL;
60 | } else {
61 | currentCard.hide();
62 | currentCard = cards.getNextCard();
63 | currentCard.showHalf();
64 | cardState = SHOW_HALF;
65 | }
66 | return false;
67 | }
68 | */
69 |
70 | function advanceCard() {
71 | if (cardState == SHOW_HALF) {
72 | currentCard.show();
73 | cardState = SHOW_FULL;
74 | } else {
75 | currentCard.hide();
76 | currentCard = cards.getNextCard();
77 | currentCard.showHalf();
78 | cardState = SHOW_HALF;
79 | }
80 | }
81 |
82 | $(document).keypress(function (evt) {
83 | keychar = evt.which;
84 | // alert(keychar);
85 | if (keychar != 32) {
86 | return true;
87 | }
88 |
89 | advanceCard();
90 | return false;
91 | }
92 | );
93 |
94 | /*
95 | $(document).click(function(evt) {
96 | // needed for iPad, Android with no keyboard!
97 | advanceCard();
98 | return false;
99 | }
100 | );
101 | */
102 |
103 | document.ontouchend = function (evt) {
104 | // needed for iPad, Android with no keyboard!
105 | advanceCard();
106 | return false;
107 | }
108 |
109 | function convertHalf(showHalfCard) {
110 | if (showHalfCard == "CHINESE_HALF") {
111 | return CHINESE_HALF;
112 | } else if (showHalfCard == "ENGLISH_HALF") {
113 | return ENGLISH_HALF;
114 | } else {
115 | return RANDOM_HALF
116 | }
117 | }
118 |
119 | function convertOrder(cardOrder) {
120 | if (cardOrder == "RANDOM") {
121 | return RANDOM;
122 | } else {
123 | return SEQUENTIAL;
124 | }
125 | }
126 |
127 | function Cards(cardOrder, showHalfCard) {
128 | this.cardOrder = convertOrder(cardOrder);
129 | this.showHalfCard = convertHalf(showHalfCard);
130 | this.length = 0;
131 | this.index = -1;
132 |
133 | this.getNextCard = function () {
134 | if (this.cardOrder == RANDOM) {
135 | r = Math.random();
136 | rr = r * this.length;
137 | console.log("Random #", r, rr);
138 | index = Math.floor(rr);
139 | return this[index];
140 | }
141 | if (++this.index >= this.length) {
142 | this.index = 0;
143 | }
144 | return this[this.index];
145 | }
146 |
147 | this.push = function (node) {
148 | card = new Card(node);
149 | this[this.length++] = card;
150 | }
151 |
152 | }
153 |
154 | function Card(node) {
155 | this.node = node;
156 | this.node.style.visibility = 'hidden';
157 | this.node.style.position = 'absolute';
158 | this.node.style.left = 10;
159 | this.node.style.right = 0;
160 | this.node.style.width = '50%';
161 |
162 | this.getChild = function (child) {
163 | children = this.node.getElementsByTagName("div");
164 | for (var i = 0; i < children.length; i++) {
165 | if (children[i].className == child) {
166 | return children[i];
167 | }
168 | }
169 | return null;
170 | }
171 |
172 | this.english = this.getChild("english");
173 | this.translations = this.getChild("translations");
174 | this.pinyin = this.getChild("pinyin");
175 | this.simplified = this.getChild("simplified");
176 | this.traditional = this.getChild("traditional");
177 |
178 | this.show = function () {
179 | //alert("Showing full")
180 | this.node.style.visibility = 'visible';
181 | this.setVisibility([this.pinyin,
182 | this.simplified,
183 | this.traditional],
184 | 'visible');
185 | this.setVisibility([this.english,
186 | this.translations],
187 | 'visible');
188 |
189 | }
190 |
191 | this.showChild = function (child) {
192 | child.style.visibility = 'visible';
193 | }
194 |
195 | this.hideChild = function (child) {
196 | child.style.visibility = 'hidden';
197 | }
198 |
199 | this.showHalf = function () {
200 | if (cards.showHalfCard == RANDOM_HALF) {
201 | if (Math.random() < 0.5) {
202 | this.setVisibility([this.pinyin,
203 | this.simplified,
204 | this.traditional],
205 | 'hidden');
206 | this.setVisibility([this.english,
207 | this.translations],
208 | 'visible');
209 | } else {
210 | this.setVisibility([this.pinyin,
211 | this.simplified,
212 | this.traditional],
213 | 'visible');
214 | this.setVisibility([this.english,
215 | this.translations],
216 | 'hidden');
217 | }
218 | } else if (cards.showHalfCard == CHINESE_HALF) {
219 | this.setVisibility([this.pinyin,
220 | this.simplified,
221 | this.traditional],
222 | 'visible');
223 | this.setVisibility([this.english,
224 | this.translations],
225 | 'hidden');
226 | } else {
227 | this.setVisibility([this.pinyin,
228 | this.simplified,
229 | this.traditional],
230 | 'hidden');
231 | this.setVisibility([this.english,
232 | this.translations],
233 | 'visible');
234 | }
235 | this.node.style.visibility = 'visible';
236 | }
237 |
238 | this.hide = function () {
239 | this.node.style.visibility = 'hidden';
240 | this.setVisibility([this.pinyin,
241 | this.simplified,
242 | this.traditional],
243 | 'hidden');
244 | this.setVisibility([this.english,
245 | this.translations],
246 | 'hidden');
247 | }
248 |
249 | this.setVisibility = function (nodes, state) {
250 | for (n = 0; n < nodes.length; n++) {
251 | nodes[n].style.visibility = state;
252 | }
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/ch10/jscript/sorttable.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/network-prog-with-go-2e/d8b0fd1f54fd2710e8e8a65ad804cbc2e7f63831/ch10/jscript/sorttable.js
--------------------------------------------------------------------------------
/ch10/pinyinformatter.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | func PinyinFormatter(args ...any) string {
9 | ok := false
10 | var s string
11 | if len(args) == 1 {
12 | s, ok = args[0].(string)
13 | }
14 | if !ok {
15 | s = fmt.Sprint(args...)
16 | }
17 | fmt.Println("Formatting func " + s)
18 | // the string may consist of several pinyin words
19 | // each one needs to be changed separately and then
20 | // added back together
21 | words := strings.Fields(s)
22 |
23 | for n, word := range words {
24 | // convert "u:" to "ü" if present
25 | uColon := strings.Index(word, "u:")
26 | if uColon != -1 {
27 | parts := strings.SplitN(word, "u:", 2)
28 | word = parts[0] + "ü" + parts[1]
29 | }
30 | println(word)
31 | // get last character, will be the tone if present
32 | chars := []rune(word)
33 | tone := chars[len(chars)-1]
34 | if tone == '5' {
35 | // there is no accent for tone 5
36 | words[n] = string(chars[0 : len(chars)-1])
37 | println("lost accent on", words[n])
38 | continue
39 | }
40 | if tone < '1' || tone > '4' {
41 | // not a tone value
42 | continue
43 | }
44 | words[n] = addAccent(word, int(tone))
45 | }
46 | s = strings.Join(words, ` `)
47 | return s
48 | }
49 |
50 | var (
51 | // maps 'a1' to '\u0101' etc
52 | aAccent = map[int]rune{
53 | '1': '\u0101',
54 | '2': '\u00e1',
55 | '3': '\u01ce',
56 | '4': '\u00e0'}
57 | eAccent = map[int]rune{
58 | '1': '\u0113',
59 | '2': '\u00e9',
60 | '3': '\u011b',
61 | '4': '\u00e8'}
62 | iAccent = map[int]rune{
63 | '1': '\u012b',
64 | '2': '\u00ed',
65 | '3': '\u01d0',
66 | '4': '\u00ec'}
67 | oAccent = map[int]rune{
68 | '1': '\u014d',
69 | '2': '\u00f3',
70 | '3': '\u01d2',
71 | '4': '\u00f2'}
72 | uAccent = map[int]rune{
73 | '1': '\u016b',
74 | '2': '\u00fa',
75 | '3': '\u01d4',
76 | '4': '\u00f9'}
77 | üAccent = map[int]rune{
78 | '1': 'ǖ',
79 | '2': 'ǘ',
80 | '3': 'ǚ',
81 | '4': 'ǜ'}
82 | )
83 |
84 | func addAccent(word string, tone int) string {
85 | /*
86 | * Based on "Where do the tone marks go?"
87 | * at http://www.pinyin.info/rules/where.html
88 | */
89 |
90 | n := strings.Index(word, "a")
91 | if n != -1 {
92 | aAcc := aAccent[tone]
93 | // replace 'a' with its tone version
94 | word = word[0:n] + string(aAcc) + word[(n+1):len(word)-1]
95 | } else {
96 | n := strings.Index(word, "e")
97 | if n != -1 {
98 | eAcc := eAccent[tone]
99 | word = word[0:n] + string(eAcc) +
100 | word[(n+1):len(word)-1]
101 | } else {
102 | n = strings.Index(word, "ou")
103 | if n != -1 {
104 | oAcc := oAccent[tone]
105 | word = word[0:n] + string(oAcc) + "u" +
106 | word[(n+2):len(word)-1]
107 | } else {
108 | chars := []rune(word)
109 | length := len(chars)
110 | // put tone onthe last vowel
111 | L:
112 | for n := range chars {
113 | m := length - n - 1
114 | switch chars[m] {
115 | case 'i':
116 | chars[m] = iAccent[tone]
117 | break L
118 | case 'o':
119 | chars[m] = oAccent[tone]
120 | break L
121 | case 'u':
122 | chars[m] = uAccent[tone]
123 | break L
124 | case 'ü':
125 | chars[m] = üAccent[tone]
126 | break L
127 | default:
128 | }
129 | }
130 | word = string(chars[0 : len(chars)-1])
131 | }
132 | }
133 | }
134 | return word
135 | }
136 |
--------------------------------------------------------------------------------
/ch10/server.go:
--------------------------------------------------------------------------------
1 | /* Server
2 | */
3 |
4 | package main
5 |
6 | import (
7 | "fmt"
8 | "html/template"
9 | "log"
10 | "net/http"
11 | "os"
12 | )
13 |
14 | const (
15 | DefaultSet = "common_words"
16 | DefaultAmount = "Random"
17 | ActionShow = "Show cards in set"
18 | ActionList = "List words in set"
19 | ActionUnknown = "Unknown action"
20 | URLFlashCardSetsPath = "flashcardSets"
21 | FlashCardPage = "flashcards.html"
22 | ListFlashCardPage = "list" + FlashCardPage
23 | ShowFlashCardPage = "show" + FlashCardPage
24 | ListWordsPage = "listwords.html"
25 | CardOrderSequential = "Sequential"
26 | CardOrderRandom = "Random"
27 | )
28 |
29 | var showHalf = map[string]string{
30 | "Random": "RANDOM_HALF",
31 | "English": "ENGLISH_HALF",
32 | "Chinese": "CHINESE_HALF",
33 | }
34 |
35 | func main() {
36 | if len(os.Args) != 2 {
37 | log.Fatalln("Usage: ", os.Args[0], ":port")
38 | }
39 | port := os.Args[1]
40 |
41 | http.HandleFunc("/", listFlashCards)
42 | fileServer := http.StripPrefix("/jscript/", http.FileServer(http.Dir("jscript")))
43 | http.Handle("/jscript/", fileServer)
44 | fileServer = http.StripPrefix("/html/", http.FileServer(http.Dir("html")))
45 | http.Handle("/html/", fileServer)
46 | fileServer = http.StripPrefix("/css/", http.FileServer(http.Dir("css")))
47 | http.Handle("/css/", fileServer)
48 |
49 | http.HandleFunc("/"+FlashCardPage, listFlashCards)
50 | http.HandleFunc("/"+URLFlashCardSetsPath, manageFlashCards)
51 |
52 | // deliver requests to the handlers
53 | err := http.ListenAndServe(port, nil)
54 | checkError(err)
55 | }
56 |
57 | func listFlashCards(rw http.ResponseWriter, req *http.Request) {
58 | flashCardsNames := ListFlashCardsNames()
59 | t, err := template.ParseFiles("html/" + ListFlashCardPage)
60 |
61 | if err != nil {
62 | httpErrorHandler(rw, err)
63 | return
64 | }
65 |
66 | t.Execute(rw, flashCardsNames)
67 | }
68 |
69 | /*
70 | * Called from listflashcards.html on form submission
71 | */
72 | func manageFlashCards(rw http.ResponseWriter, req *http.Request) {
73 | set := req.FormValue("flashcardSets")
74 | order := req.FormValue("order")
75 | action := req.FormValue("submit")
76 | half := req.FormValue("half")
77 |
78 | //if unset
79 | //http://localhost:8000/flashcardSets?flashcardSets=common_words&order=Random&half=Random&submit=Show+cards+in+set
80 | if len(set) == 0 {
81 | set = DefaultSet
82 | order = DefaultAmount
83 | action = ActionShow
84 | half = DefaultAmount
85 | }
86 |
87 | cardname := URLFlashCardSetsPath + "/" + set
88 |
89 | fmt.Printf("Set %s, order %s, action %s, half %s, cardname %s\n", set, order, action, half, cardname)
90 |
91 | switch action {
92 | case ActionShow:
93 | showFlashCards(rw, cardname, order, half)
94 | case ActionList:
95 | listWords(rw, cardname)
96 | default:
97 | fmt.Println(ActionUnknown)
98 | }
99 | }
100 |
101 | func showFlashCards(rw http.ResponseWriter, cardname, order, half string) {
102 | cards := new(FlashCards)
103 | content, err := os.Open(cardname)
104 | checkError(err)
105 | LoadJSON(content, &cards)
106 |
107 | switch order {
108 | case CardOrderSequential:
109 | cards.CardOrder = "SEQUENTIAL"
110 | default:
111 | cards.CardOrder = "RANDOM"
112 | }
113 |
114 | if v, ok := showHalf[half]; ok {
115 | cards.ShowHalf = v
116 | } else {
117 | cards.ShowHalf = showHalf["Chinese"]
118 | }
119 |
120 | fmt.Printf("Loading card %s, half %s, loaded # of %d, card name %s\n", cardname, half, len(cards.Cards), cards.Name)
121 |
122 | t, err := template.New(ShowFlashCardPage).Funcs(template.FuncMap{"pinyin": PinyinFormatter}).ParseFiles("html/" + ShowFlashCardPage)
123 |
124 | if err != nil {
125 | httpErrorHandler(rw, err)
126 | return
127 | }
128 |
129 | err = t.Execute(rw, cards)
130 |
131 | if err != nil {
132 | httpErrorHandler(rw, err)
133 | return
134 | }
135 | }
136 |
137 | func listWords(rw http.ResponseWriter, cardname string) {
138 | cards := new(FlashCards)
139 | content, err := os.Open(cardname)
140 | checkError(err)
141 | LoadJSON(content, &cards)
142 |
143 | fmt.Printf("Loading card name %s, loaded cards %d, card name %s\n", cardname, len(cards.Cards), cards.Name)
144 |
145 | t, err := template.New(ListWordsPage).Funcs(template.FuncMap{"pinyin": PinyinFormatter}).ParseFiles("html/" + ListWordsPage)
146 |
147 | if err != nil {
148 | httpErrorHandler(rw, err)
149 | return
150 | }
151 |
152 | err = t.Execute(rw, cards)
153 |
154 | if err != nil {
155 | httpErrorHandler(rw, err)
156 | return
157 | }
158 | }
159 |
160 | func httpErrorHandler(rw http.ResponseWriter, err error) {
161 | http.Error(rw, err.Error(), http.StatusInternalServerError)
162 | }
163 |
164 | func checkError(err error) {
165 | if err != nil {
166 | log.Fatalln("Fatal error ", err.Error())
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/ch11/escapestring.go:
--------------------------------------------------------------------------------
1 | /*
2 | * This program serves a file in preformatted, code layout
3 | * Useful for showing program text, properly escaping special
4 | * characters like '<', '>' and '&'
5 | */
6 |
7 | package main
8 |
9 | import (
10 | "fmt"
11 | "html"
12 | "log"
13 | "net/http"
14 | "os"
15 | )
16 |
17 | func main() {
18 | http.HandleFunc("/", escapeString)
19 | err := http.ListenAndServe(":8080", nil)
20 | checkError(err)
21 | }
22 | func escapeString(rw http.ResponseWriter, req *http.Request) {
23 | fmt.Println(req.URL.Path)
24 | bytes, err := os.ReadFile("." + req.URL.Path)
25 | if err != nil {
26 | rw.WriteHeader(http.StatusNotFound)
27 | return
28 | }
29 | escapedStr := html.EscapeString(string(bytes))
30 | htmlText := "" +
31 | escapedStr +
32 | "
"
33 | rw.Write([]byte(htmlText))
34 | }
35 | func checkError(err error) {
36 | if err != nil {
37 | log.Fatalln("Error ", err.Error())
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ch11/readhtml.go:
--------------------------------------------------------------------------------
1 | /* Read HTML
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "golang.org/x/net/html"
8 | "io"
9 | "log"
10 | "os"
11 | "strings"
12 | )
13 |
14 | func main() {
15 | if len(os.Args) != 2 {
16 | log.Fatalln("Usage: ", os.Args[0], "file")
17 | }
18 | file := os.Args[1]
19 | bytes, err := os.ReadFile(file)
20 | checkError(err)
21 | r := strings.NewReader(string(bytes))
22 | z := html.NewTokenizer(r)
23 | depth := 0
24 | for {
25 | tt := z.Next()
26 | for n := 0; n < depth; n++ {
27 | fmt.Print(" ")
28 | }
29 | switch tt {
30 | case html.ErrorToken:
31 | if z.Err() == io.EOF {
32 | fmt.Println("EOF")
33 | } else {
34 | fmt.Println("Error ", z.Err().Error())
35 | }
36 | os.Exit(0)
37 | case html.TextToken:
38 | fmt.Println("Text: \"" + z.Token().String() + "\"")
39 | case html.StartTagToken, html.EndTagToken:
40 | fmt.Println("Tag: \"" + z.Token().String() + "\"")
41 | if tt == html.StartTagToken {
42 | depth++
43 | } else {
44 | depth--
45 | }
46 | }
47 | }
48 | }
49 | func checkError(err error) {
50 | if err != nil {
51 | log.Fatalln("Fatal error ", err.Error())
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ch11/sample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Test HTML
4 |
5 |
6 | Header one
7 |
8 | Test para
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ch12/marshal.go:
--------------------------------------------------------------------------------
1 | /* Marshal
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/xml"
7 | "fmt"
8 | )
9 |
10 | type Person struct {
11 | XMLName xml.Name `xml:"person"`
12 | Name Name `xml:"name"`
13 | Email []Email `xml:"email"`
14 | }
15 | type Name struct {
16 | Family string `xml:"family"`
17 | Personal string `xml:"personal"`
18 | }
19 | type Email struct {
20 | Kind string "attr"
21 | Address string "chardata"
22 | }
23 |
24 | func main() {
25 | person := Person{
26 | Name: Name{Family: "Newmarch", Personal: "Jan"},
27 | Email: []Email{Email{Kind: "home", Address: "jan"},
28 | Email{Kind: "work", Address: "jan"}}}
29 | buff, _ := xml.Marshal(person)
30 | fmt.Println(string(buff))
31 | }
32 |
--------------------------------------------------------------------------------
/ch12/parsexml.go:
--------------------------------------------------------------------------------
1 | /* Parse XML
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/xml"
7 | "fmt"
8 | "io/ioutil"
9 | "log"
10 | "os"
11 | "strings"
12 | )
13 |
14 | func main() {
15 | if len(os.Args) != 2 {
16 | log.Fatalln("Usage: ", os.Args[0], "file")
17 | }
18 | file := os.Args[1]
19 | bytes, err := ioutil.ReadFile(file)
20 | checkError(err)
21 | r := strings.NewReader(string(bytes))
22 | parser := xml.NewDecoder(r)
23 | depth := 0
24 | for {
25 | token, err := parser.Token()
26 | if err != nil {
27 | break
28 | }
29 | switch elmt := token.(type) {
30 | case xml.StartElement:
31 | name := elmt.Name.Local
32 | printElmt(name+":start", depth)
33 | depth++
34 | case xml.EndElement:
35 | depth--
36 | name := elmt.Name.Local
37 | printElmt(name+":end", depth)
38 | case xml.CharData:
39 | printElmt(string([]byte(elmt)), depth)
40 | case xml.Comment:
41 | printElmt("Comment", depth)
42 | case xml.ProcInst:
43 | printElmt("ProcInst", depth)
44 | case xml.Directive:
45 | printElmt("Directive", depth)
46 | default:
47 | fmt.Println("Unknown")
48 | }
49 | }
50 | }
51 | func printElmt(s string, depth int) {
52 | slimS := strings.TrimSpace(s)
53 | if len(slimS) == 0 {
54 | return
55 | }
56 | for n := 0; n < depth; n++ {
57 | fmt.Print(" ")
58 | }
59 | fmt.Println(slimS)
60 | }
61 | func checkError(err error) {
62 | if err != nil {
63 | log.Fatalln("Fatal error ", err.Error())
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/ch12/person.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Newmarch
4 | Jan
5 |
6 |
7 | jan@newmarch.name
8 |
9 |
10 | j.newmarch@boxhill.edu.au
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ch12/unmarshal.go:
--------------------------------------------------------------------------------
1 | /* Unmarshal
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/xml"
7 | "fmt"
8 | "log"
9 | )
10 |
11 | type Person struct {
12 | XMLName Name `xml:"person"`
13 | Name Name `xml:"name"`
14 | Email []Email `xml:"email"`
15 | }
16 |
17 | type Name struct {
18 | Family string `xml:"family"`
19 | Personal string `xml:"personal"`
20 | }
21 |
22 | type Email struct {
23 | Type string `xml:"type,attr"`
24 | Address string `xml:",chardata"`
25 | }
26 |
27 | func main() {
28 | str := `
29 |
30 |
31 | Newmarch
32 | Jan
33 |
34 |
35 | jan@newmarch.name
36 |
37 |
38 | j.newmarch@boxhill.edu.au
39 |
40 | `
41 | var person Person
42 | err := xml.Unmarshal([]byte(str), &person)
43 | checkError(err)
44 | // now use the person structure e.g.
45 | fmt.Println("Family name: \"" + person.Name.Family + "\"")
46 | for _, email := range person.Email {
47 | fmt.Println(email)
48 | }
49 | }
50 | func checkError(err error) {
51 | if err != nil {
52 | log.Fatalln("Fatal error ", err.Error())
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ch13/arithclient.go:
--------------------------------------------------------------------------------
1 | /* ArithClient
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net/rpc"
9 | "os"
10 | )
11 |
12 | type Args struct {
13 | A, B int
14 | }
15 | type Quotient struct {
16 | Quo, Rem int
17 | }
18 |
19 | func main() {
20 | if len(os.Args) != 2 {
21 | fmt.Println("Usage: ", os.Args[0], "server")
22 | os.Exit(1)
23 | }
24 | serverAddress := os.Args[1]
25 | client, err := rpc.DialHTTP("tcp", serverAddress+":1234")
26 | if err != nil {
27 | log.Fatal("dialing:", err)
28 | }
29 | // Synchronous call
30 | args := Args{17, 8}
31 | var reply int
32 | err = client.Call("Arith.Multiply", args, &reply)
33 | if err != nil {
34 | log.Fatal("arith error:", err)
35 | }
36 | fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
37 | var quot Quotient
38 | err = client.Call("Arith.Divide", args, ")
39 | if err != nil {
40 | log.Fatal("arith error:", err)
41 | }
42 | fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B,
43 | quot.Quo, quot.Rem)
44 | }
45 |
--------------------------------------------------------------------------------
/ch13/arithserver.go:
--------------------------------------------------------------------------------
1 | /* ArithServer
2 | */
3 | package main
4 |
5 | import (
6 | "errors"
7 | "fmt"
8 | "net/http"
9 | "net/rpc"
10 | )
11 |
12 | type Values struct {
13 | A, B int
14 | }
15 | type Quotient struct {
16 | Quo, Rem int
17 | }
18 | type Arith int
19 |
20 | func (t *Arith) Multiply(args *Values, reply *int) error {
21 | *reply = args.A * args.B
22 | return nil
23 | }
24 | func (t *Arith) Divide(args *Values, quo *Quotient) error {
25 | if args.B == 0 {
26 | return errors.New("divide by zero")
27 | }
28 | quo.Quo = args.A / args.B
29 | quo.Rem = args.A % args.B
30 | return nil
31 | }
32 | func main() {
33 | arith := new(Arith)
34 | rpc.Register(arith)
35 | rpc.HandleHTTP()
36 | err := http.ListenAndServe(":1234", nil)
37 | if err != nil {
38 | fmt.Println(err.Error())
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ch13/jsonarithclient.go:
--------------------------------------------------------------------------------
1 | /* JSONArithCLient
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net/rpc/jsonrpc"
9 | "os"
10 | )
11 |
12 | type Args struct {
13 | A, B int
14 | }
15 | type Quotient struct {
16 | Quo, Rem int
17 | }
18 |
19 | func main() {
20 | if len(os.Args) != 2 {
21 | log.Fatalln("Usage: ", os.Args[0], "server:port")
22 | }
23 | service := os.Args[1]
24 | client, err := jsonrpc.Dial("tcp", service)
25 | if err != nil {
26 | log.Fatalln("dialing:", err)
27 | }
28 | // Synchronous call
29 | args := Args{17, 8}
30 | var reply int
31 | err = client.Call("Arith.Multiply", args, &reply)
32 | if err != nil {
33 | log.Fatalln("arith error:", err)
34 | }
35 | fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
36 | var quot Quotient
37 | err = client.Call("Arith.Divide", args, ")
38 | if err != nil {
39 | log.Fatalln("arith error:", err)
40 | }
41 | fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B,
42 | quot.Quo, quot.Rem)
43 | }
44 |
--------------------------------------------------------------------------------
/ch13/jsonarithserver.go:
--------------------------------------------------------------------------------
1 | /* JSONArithServer
2 | */
3 | package main
4 |
5 | import (
6 | "errors"
7 | "log"
8 | "net"
9 | "net/rpc"
10 | "net/rpc/jsonrpc"
11 | )
12 |
13 | type Args struct {
14 | A, B int
15 | }
16 | type Quotient struct {
17 | Quo, Rem int
18 | }
19 | type Arith int
20 |
21 | func (t *Arith) Multiply(args *Args, reply *int) error {
22 | *reply = args.A * args.B
23 | return nil
24 | }
25 | func (t *Arith) Divide(args *Args, quo *Quotient) error {
26 | if args.B == 0 {
27 | return errors.New("divide by zero")
28 | }
29 | quo.Quo = args.A / args.B
30 | quo.Rem = args.A % args.B
31 | return nil
32 | }
33 | func main() {
34 | arith := new(Arith)
35 | rpc.Register(arith)
36 | tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
37 | checkError(err)
38 | listener, err := net.ListenTCP("tcp", tcpAddr)
39 | checkError(err)
40 | /* This works:
41 | rpc.Accept(listener)
42 | */
43 | /* and so does this:
44 | */
45 | for {
46 | conn, err := listener.Accept()
47 | if err != nil {
48 | continue
49 | }
50 | jsonrpc.ServeConn(conn)
51 | }
52 | }
53 | func checkError(err error) {
54 | if err != nil {
55 | log.Fatalln("Fatal error ", err.Error())
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/ch13/tcparithclient.go:
--------------------------------------------------------------------------------
1 | /* TCPArithClient
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net/rpc"
9 | "os"
10 | )
11 |
12 | type Args struct {
13 | A, B int
14 | }
15 | type Quotient struct {
16 | Quo, Rem int
17 | }
18 |
19 | func main() {
20 | if len(os.Args) != 2 {
21 | log.Fatalln("Usage: ", os.Args[0], "server:port")
22 | }
23 | service := os.Args[1]
24 | client, err := rpc.Dial("tcp", service)
25 | if err != nil {
26 | log.Fatalln("dialing:", err)
27 | }
28 | // Synchronous call
29 | args := Args{17, 8}
30 | var reply int
31 | err = client.Call("Arith.Multiply", args, &reply)
32 | if err != nil {
33 | log.Fatalln("arith error:", err)
34 | }
35 | fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
36 | var quot Quotient
37 | err = client.Call("Arith.Divide", args, ")
38 | if err != nil {
39 | log.Fatalln("arith error:", err)
40 | }
41 | fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B,
42 | quot.Quo, quot.Rem)
43 | }
44 |
--------------------------------------------------------------------------------
/ch13/tcparithserver.go:
--------------------------------------------------------------------------------
1 | /* TCPArithServer
2 | */
3 | package main
4 |
5 | import (
6 | "errors"
7 | "log"
8 | "net"
9 | "net/rpc"
10 | )
11 |
12 | type Args struct {
13 | A, B int
14 | }
15 | type Quotient struct {
16 | Quo, Rem int
17 | }
18 | type Arith int
19 |
20 | func (t *Arith) Multiply(args *Args, reply *int) error {
21 | *reply = args.A * args.B
22 | return nil
23 | }
24 | func (t *Arith) Divide(args *Args, quo *Quotient) error {
25 | if args.B == 0 {
26 | return errors.New("divide by zero")
27 | }
28 | quo.Quo = args.A / args.B
29 | quo.Rem = args.A % args.B
30 | return nil
31 | }
32 | func main() {
33 | arith := new(Arith)
34 | rpc.Register(arith)
35 | tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
36 | checkError(err)
37 | listener, err := net.ListenTCP("tcp", tcpAddr)
38 | checkError(err)
39 | /* This works:
40 | rpc.Accept(listener)
41 | */
42 | /* and so does this:
43 | */
44 | for {
45 | conn, err := listener.Accept()
46 | if err != nil {
47 | continue
48 | }
49 | rpc.ServeConn(conn)
50 | }
51 | }
52 | func checkError(err error) {
53 | if err != nil {
54 | log.Fatalln("Fatal error ", err.Error())
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ch14/client.go:
--------------------------------------------------------------------------------
1 | /* Client
2 | */
3 |
4 | package main
5 |
6 | import (
7 | "encoding/xml"
8 | "fmt"
9 | "io"
10 | "log"
11 | "net/http"
12 | "net/http/httputil"
13 | "net/url"
14 | "os"
15 | "strings"
16 | )
17 |
18 | const flashcard_xml string = "application/x.flashcards+xml"
19 | const flashcard_json string = "application/x.flashcards+json"
20 |
21 | type FlashcardSets struct {
22 | XMLName string `xml:"cardsets"`
23 | CardSet []CardSet `xml:"cardset"`
24 | }
25 |
26 | type CardSet struct {
27 | XMLName string `xml:"cardset"`
28 | Name string `xml:"name"`
29 | Link string `xml:"href,attr"`
30 | Cards []Card `xml:"card"`
31 | }
32 |
33 | type Card struct {
34 | Name string `xml:"name"`
35 | Link string `xml:"href,attr"`
36 | }
37 |
38 | func getter(url *url.URL, client *http.Client, acceptType string) *http.Response {
39 | request, err := http.NewRequest("GET", url.String(), nil)
40 | checkError(err)
41 |
42 | if acceptType != "" {
43 | request.Header.Add("Accept", flashcard_xml)
44 | }
45 | response, err := client.Do(request)
46 | checkError(err)
47 | if response.StatusCode != http.StatusOK {
48 | log.Fatalln(err, response)
49 | }
50 |
51 | fmt.Println("The response header is")
52 | b, _ := httputil.DumpResponse(response, false)
53 | fmt.Print(string(b))
54 | return response
55 | }
56 |
57 | func getOneFlashcard(url *url.URL, client *http.Client) string {
58 | // Get the card as a string, don't do anything with it
59 | response := getter(url, client, "")
60 |
61 | body, err := io.ReadAll(response.Body)
62 | checkError(err)
63 | content := string(body[:])
64 | //fmt.Printf("Body is %s", content)
65 |
66 | return content
67 | }
68 |
69 | func getOneFlashcardSet(url *url.URL, client *http.Client) CardSet {
70 | // Get one set of cards
71 | response := getter(url, client, flashcard_xml)
72 |
73 | body, err := io.ReadAll(response.Body)
74 | content := string(body[:])
75 | fmt.Printf("Body is %s", content)
76 |
77 | var sets CardSet
78 | contentType := getContentType(response)
79 | if contentType == "XML" {
80 |
81 | err = xml.Unmarshal(body, &sets)
82 | checkError(err)
83 | fmt.Println("XML: ", sets)
84 | return sets
85 | }
86 | /* else if contentType == "JSON" {
87 | var sets FlashcardSetsJson
88 | err = json.Unmarshal(body, &sets)
89 | checkError(err)
90 | fmt.Println("JSON: ", sets)
91 | }
92 | */
93 | return sets
94 | }
95 |
96 | func getFlashcardSets(url *url.URL, client *http.Client) FlashcardSets {
97 | // Get the toplevel /
98 | response := getter(url, client, flashcard_xml)
99 |
100 | body, err := io.ReadAll(response.Body)
101 | content := string(body[:])
102 | fmt.Printf("Body is %s", content)
103 |
104 | var sets FlashcardSets
105 | contentType := getContentType(response)
106 | if contentType == "XML" {
107 | err = xml.Unmarshal(body, &sets)
108 | checkError(err)
109 | fmt.Println("XML: ", sets)
110 | return sets
111 | }
112 | return sets
113 | }
114 |
115 | func createFlashcardSet(url1 *url.URL, client *http.Client, name string) string {
116 | data := make(url.Values)
117 | data[`name`] = []string{name}
118 | response, err := client.PostForm(url1.String(), data)
119 | checkError(err)
120 | if response.StatusCode != http.StatusCreated {
121 | fmt.Println(`Error: `, response.Status)
122 | return ``
123 | }
124 | body, err := io.ReadAll(response.Body)
125 | content := string(body[:])
126 | return content
127 | }
128 |
129 | func main() {
130 | if len(os.Args) != 2 {
131 | log.Fatalln("Usage: ", os.Args[0], "http://host:port/page")
132 | }
133 | url, err := url.Parse(os.Args[1])
134 | checkError(err)
135 |
136 | client := &http.Client{}
137 |
138 | // Step 1: get a list of flashcard sets
139 | flashcardSets := getFlashcardSets(url, client)
140 | fmt.Println("Step 1: ", flashcardSets)
141 |
142 | // Step 2: try to create a new flashcard set
143 | new_url := createFlashcardSet(url, client, `NewSet`)
144 | fmt.Println("Step 2: New flashcard set has URL: ", new_url)
145 |
146 | // Step 3: using the first flashcard set,
147 | // get the list of cards in it
148 | set_url, _ := url.Parse(os.Args[1] + flashcardSets.CardSet[0].Link)
149 |
150 | fmt.Println("Asking for flashcard set URL: ", set_url.String())
151 | oneFlashcardSet := getOneFlashcardSet(set_url, client)
152 | fmt.Println("Step 3:", oneFlashcardSet)
153 |
154 | // Step 4: get the contents of one flashcard
155 | // be lazy, just get as text/plain and
156 | // don't do anything with it
157 | card_url, _ := url.Parse(os.Args[1] + oneFlashcardSet.Cards[0].Link)
158 | fmt.Println("Asking for URL: ", card_url.String())
159 | oneFlashcard := getOneFlashcard(card_url, client)
160 | fmt.Println("Step 4", oneFlashcard)
161 | }
162 |
163 | func getContentType(response *http.Response) string {
164 | contentType := response.Header.Get("Content-Type")
165 | if strings.Contains(contentType, flashcard_xml) {
166 | return "XML"
167 | }
168 | if strings.Contains(contentType, flashcard_json) {
169 | return "JSON"
170 | }
171 | return ""
172 | }
173 |
174 | func checkError(err error) {
175 | if err != nil {
176 | log.Fatalln("Fatal error ", err.Error())
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/ch14/clientjson.go:
--------------------------------------------------------------------------------
1 | /* ClientGet
2 | */
3 |
4 | package main
5 |
6 | import (
7 | "io"
8 | "encoding/json"
9 | "fmt"
10 | "log"
11 | "net/http"
12 | "net/http/httputil"
13 | "net/url"
14 | "os"
15 | "strings"
16 | )
17 |
18 | const flashcard_xml string = "application/x.flashcards+xml"
19 | const flashcard_json string = "application/x.flashcards+json"
20 |
21 | type FlashcardSets struct {
22 | XMLName string `xml:"cardsets"`
23 | CardSet []CardSet `xml:"cardset"`
24 | }
25 |
26 | type CardSet struct {
27 | XMLName string `xml:"cardset"`
28 | Name string `xml:"name"`
29 | Link string `xml:"href,attr"`
30 | Cards []Card `xml:"card"`
31 | }
32 |
33 | type Card struct {
34 | Name string `xml:"name"`
35 | Link string `xml:"href,attr"`
36 | }
37 |
38 | type FlashcardSetsJson struct {
39 | CardSet []CardSetJson `json:"cardsets"`
40 | }
41 | type CardSetJson struct {
42 | Name string `json:"name"`
43 | Link string `json:"@id"`
44 | Cards []CardJson `json:"cardset,omitempty"`
45 | }
46 | type CardJson struct {
47 | Name string `json:"name"`
48 | Link string `json:"@id"`
49 | }
50 |
51 | func getOneFlashcard(url *url.URL, client *http.Client) string {
52 | // Get the card as a string, don't do anything with it
53 | request, err := http.NewRequest("GET", url.String(), nil)
54 | checkError(err)
55 |
56 | response, err := client.Do(request)
57 | checkError(err)
58 | if response.StatusCode != http.StatusOK {
59 | fmt.Println(response.Status)
60 | fmt.Println(response.Header)
61 | checkError(err)
62 | }
63 |
64 | fmt.Println("The response header is")
65 | b, _ := httputil.DumpResponse(response, false)
66 | fmt.Print(string(b))
67 |
68 | body, err := io.ReadAll(response.Body)
69 | content := string(body[:])
70 |
71 | return content
72 | }
73 |
74 | func getOneFlashcardSet(url *url.URL, client *http.Client) CardSetJson {
75 | // Get one set of cards
76 | request, err := http.NewRequest("GET", url.String(), nil)
77 | checkError(err)
78 |
79 | // only accept our media types
80 | request.Header.Add("Accept", flashcard_json)
81 | response, err := client.Do(request)
82 | checkError(err)
83 | if response.StatusCode != http.StatusOK {
84 | fmt.Println(response.Status)
85 | fmt.Println(response.Header)
86 | checkError(err)
87 | }
88 |
89 | fmt.Println("The response header is")
90 | b, _ := httputil.DumpResponse(response, false)
91 | fmt.Print(string(b))
92 |
93 | body, err := io.ReadAll(response.Body)
94 | content := string(body[:])
95 | fmt.Printf("Body is %s", content)
96 |
97 | var sets CardSetJson
98 | contentType := getContentType(response)
99 | if contentType == "JSON" {
100 | err = json.Unmarshal(body, &sets)
101 | checkError(err)
102 | fmt.Println("JSON: ", sets)
103 | }
104 |
105 | return sets
106 | }
107 |
108 | func getFlashcardSets(url *url.URL, client *http.Client) FlashcardSetsJson {
109 | // Get the toplevel /
110 | request, err := http.NewRequest("GET", url.String(), nil)
111 | checkError(err)
112 |
113 | // only accept our media types
114 | request.Header.Add("Accept", flashcard_json)
115 | response, err := client.Do(request)
116 | checkError(err)
117 | if response.StatusCode != http.StatusOK {
118 | fmt.Println(response.Status)
119 | fmt.Println(response.Header)
120 |
121 | checkError(err)
122 | }
123 |
124 | fmt.Println("The response header is")
125 | b, _ := httputil.DumpResponse(response, false)
126 | fmt.Print(string(b))
127 |
128 | body, err := io.ReadAll(response.Body)
129 | content := string(body[:])
130 | fmt.Printf("Body is %s", content)
131 |
132 | var sets FlashcardSetsJson
133 | contentType := getContentType(response)
134 | if contentType == "JSON" {
135 | err = json.Unmarshal(body, &sets)
136 | checkError(err)
137 | fmt.Println("JSON: ", sets)
138 | }
139 | return sets
140 | }
141 |
142 | func main() {
143 | if len(os.Args) != 2 {
144 | log.Fatalln("Usage: ", os.Args[0], "http://host:port/page")
145 | }
146 | url, err := url.Parse(os.Args[1])
147 | checkError(err)
148 |
149 | client := &http.Client{}
150 |
151 | flashcardSets := FlashcardSetsJson{}
152 | flashcardSets.CardSet = []CardSetJson{CardSetJson{}}
153 | flashcardSets.CardSet[0].Name = `n1`
154 | flashcardSets.CardSet[0].Link = `l1`
155 | //flashcardSets.CardSet[0].Cards = []CardJson{CardJson{Name: `n`, Link: `l`}}
156 | bytes, _ := json.Marshal(flashcardSets)
157 | fmt.Println(string(bytes[:]))
158 |
159 | url = url
160 | client = client
161 |
162 | // Step 1: get a list of flashcard sets
163 | flashcardSets = getFlashcardSets(url, client)
164 | fmt.Println("Step1, Cardsets are: ", flashcardSets)
165 |
166 | // Step 2: using the first flashcard set,
167 | // get the list of cards in it
168 | set_url, _ := url.Parse(os.Args[1] + flashcardSets.CardSet[0].Link)
169 | oneFlashcardSet := getOneFlashcardSet(set_url, client)
170 | fmt.Println("Step 2: ", oneFlashcardSet)
171 |
172 | // Step 3: get the contents of one flashcard
173 | // be lazy, just get as text/plain and
174 | // don't do anything with it
175 | card_url, _ := url.Parse(os.Args[1] + oneFlashcardSet.Cards[0].Link)
176 | fmt.Println("Asking for URL: ", card_url.String())
177 | oneFlashcard := getOneFlashcard(card_url, client)
178 | fmt.Println("Step 3", oneFlashcard)
179 | }
180 |
181 | func getContentType(response *http.Response) string {
182 | contentType := response.Header.Get("Content-Type")
183 | if strings.Contains(contentType, flashcard_xml) {
184 | return "XML"
185 | }
186 | if strings.Contains(contentType, flashcard_json) {
187 | return "JSON"
188 | }
189 | return ""
190 | }
191 |
192 | func checkError(err error) {
193 | if err != nil {
194 | log.Fatalln("Fatal error ", err.Error())
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/ch14/flashcardsets/CommonWords/你好:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u4f60\u597d", "Dictionary": {"Entries": [{"Traditional": "\u4f60\u597d", "Pinyin": "ni3 hao3", "Translations": ["hello", "hi", "how are you?"], "Simplified": "\u4f60\u597d"}]}, "English": "hello"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/CommonWords/你好吗:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u4f60\u597d\u5417", "Dictionary": {"Entries": [{"Traditional": "\u4f60\u597d\u55ce", "Pinyin": "ni3 hao3 ma5", "Translations": ["How are you?", "Are you well?"], "Simplified": "\u4f60\u597d\u5417"}]}, "English": "How are you?"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/CommonWords/再见:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u518d\u89c1", "Dictionary": {"Entries": [{"Traditional": "\u518d\u898b", "Pinyin": "zai4 jian4", "Translations": ["goodbye", "see you again later"], "Simplified": "\u518d\u89c1"}]}, "English": "goodbye"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/CommonWords/厕所:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u5395\u6240", "Dictionary": {"Entries": [{"Traditional": "\u5ec1\u6240", "Pinyin": "ce4 suo3", "Translations": ["toilet", "lavatory", "CL:\u9593|\u95f4[jian1],\u8655|\u5904[chu4]"], "Simplified": "\u5395\u6240"}]}, "English": "toilet"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/CommonWords/喂:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u5582", "Dictionary": {"Entries": [{"Traditional": "\u5582", "Pinyin": "wei4", "Translations": ["hello (interj., esp. on telephone)", "hey", "to feed (sb or some animal)"], "Simplified": "\u5582"}]}, "English": "hello (interj., esp. on telephone)"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/CommonWords/好:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u597d", "Dictionary": {"Entries": [{"Traditional": "\u597d", "Pinyin": "hao3", "Translations": ["good", "well", "proper", "good to", "easy to", "very", "so", "(suffix indicating completion or readiness)"], "Simplified": "\u597d"}]}, "English": "good"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/CommonWords/谢谢:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u8c22\u8c22", "Dictionary": {"Entries": [{"Traditional": "\u8b1d\u8b1d", "Pinyin": "xie4 xie5", "Translations": ["to thank", "thanks"], "Simplified": "\u8c22\u8c22"}]}, "English": "thanks"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/不:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u4e0d", "Dictionary": {"Entries": [{"Traditional": "\u4e0d", "Pinyin": "bu4", "Translations": ["(negative prefix)", "not", "no"], "Simplified": "\u4e0d"}]}, "English": "no"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/也:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u4e5f", "Dictionary": {"Entries": [{"Traditional": "\u4e5f", "Pinyin": "ye3", "Translations": ["also", "too", "(in Classical Chinese) final particle implying affirmation"], "Simplified": "\u4e5f"}]}, "English": "also"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/人:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u4eba", "Dictionary": {"Entries": [{"Traditional": "\u4eba", "Pinyin": "ren2", "Translations": ["man", "person", "people", "CL:\u500b|\u4e2a[ge4],\u4f4d[wei4]"], "Simplified": "\u4eba"}]}, "English": "person"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/什么:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u4ec0\u4e48", "Dictionary": {"Entries": [{"Traditional": "\u4ec0\u9ebc", "Pinyin": "shen2 me5", "Translations": ["what?", "who?", "something", "anything"], "Simplified": "\u4ec0\u4e48"}]}, "English": "what?"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/他:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u4ed6", "Dictionary": {"Entries": [{"Traditional": "\u4ed6", "Pinyin": "ta1", "Translations": ["he or him", "(used for either sex when the sex is unknown or unimportant)", "(used before sb's name for emphasis)", "(used as a meaningless mock object)", "other", "another"], "Simplified": "\u4ed6"}]}, "English": "he or him"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/他们:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u4ed6\u4eec", "Dictionary": {"Entries": [{"Traditional": "\u4ed6\u5011", "Pinyin": "ta1 men5", "Translations": ["they"], "Simplified": "\u4ed6\u4eec"}]}, "English": "they"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/你:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u4f60", "Dictionary": {"Entries": [{"Traditional": "\u4f60", "Pinyin": "ni3", "Translations": ["you (informal, as opposed to courteous \u60a8[nin2])"], "Simplified": "\u4f60"}]}, "English": "you (informal, as opposed to courteous \u60a8[nin2])"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/去:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u53bb", "Dictionary": {"Entries": [{"Traditional": "\u53bb", "Pinyin": "qu4", "Translations": ["to go", "to go to (a place)", "to cause to go or send (sb)", "to remove", "to get rid of", "(when used either before or after a verb) to go in order to do sth", "to be apart from in space or time", "(after a verb of motion indicates movement away from the speaker)", "(used after certain verbs to indicate detachment or separation)", "(of a time or an event etc) just passed or elapsed"], "Simplified": "\u53bb"}]}, "English": "to go"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/叫:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u53eb", "Dictionary": {"Entries": [{"Traditional": "\u53eb", "Pinyin": "jiao4", "Translations": ["to shout", "to call", "to order", "to ask", "to be called", "by (indicates agent in the passive mood)"], "Simplified": "\u53eb"}]}, "English": "to be called"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/名字:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u540d\u5b57", "Dictionary": {"Entries": [{"Traditional": "\u540d\u5b57", "Pinyin": "ming2 zi5", "Translations": ["name (of a person or thing)", "CL:\u500b|\u4e2a[ge4]"], "Simplified": "\u540d\u5b57"}]}, "English": "name (of a person or thing)"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/哪:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u54ea", "Dictionary": {"Entries": [{"Traditional": "\u54ea", "Pinyin": "na3", "Translations": ["how", "which"], "Simplified": "\u54ea"}]}, "English": "which"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/囶:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u56f6", "Dictionary": {"Entries": [{"Traditional": "\u56f6", "Pinyin": "guo2", "Translations": ["variant of \u570b|\u56fd", "country"], "Simplified": "\u56f6"}]}, "English": "country"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/国家:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u56fd\u5bb6", "Dictionary": {"Entries": [{"Traditional": "\u570b\u5bb6", "Pinyin": "guo2 jia1", "Translations": ["country", "nation", "state", "CL:\u500b|\u4e2a[ge4]"], "Simplified": "\u56fd\u5bb6"}]}, "English": "country"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/她:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u5979", "Dictionary": {"Entries": [{"Traditional": "\u5979", "Pinyin": "ta1", "Translations": ["she"], "Simplified": "\u5979"}]}, "English": "she"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/她们:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u5979\u4eec", "Dictionary": {"Entries": [{"Traditional": "\u5979\u5011", "Pinyin": "ta1 men5", "Translations": ["they", "them (for females)"], "Simplified": "\u5979\u4eec"}]}, "English": "they"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/姓:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u59d3", "Dictionary": {"Entries": [{"Traditional": "\u59d3", "Pinyin": "xing4", "Translations": ["family name", "surname", "name", "CL:\u500b|\u4e2a[ge4]"], "Simplified": "\u59d3"}]}, "English": "surname"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/学生:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u5b66\u751f", "Dictionary": {"Entries": [{"Traditional": "\u5b78\u751f", "Pinyin": "xue2 sheng5", "Translations": ["student", "school child"], "Simplified": "\u5b66\u751f"}]}, "English": "student"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/小姐:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u5c0f\u59d0", "Dictionary": {"Entries": [{"Traditional": "\u5c0f\u59d0", "Pinyin": "xiao3 jie5", "Translations": ["young lady", "miss", "CL:\u500b|\u4e2a[ge4],\u4f4d[wei4]"], "Simplified": "\u5c0f\u59d0"}]}, "English": "miss"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/很:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u5f88", "Dictionary": {"Entries": [{"Traditional": "\u5f88", "Pinyin": "hen3", "Translations": ["(adverb of degree)", "quite", "very", "awfully"], "Simplified": "\u5f88"}]}, "English": "very"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/恁:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u6041", "Dictionary": {"Entries": [{"Traditional": "\u6041", "Pinyin": "nin2", "Translations": ["you (polite)"], "Simplified": "\u6041"}]}, "English": "you (polite)"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/想:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u60f3", "Dictionary": {"Entries": [{"Traditional": "\u60f3", "Pinyin": "xiang3", "Translations": ["to think", "to believe", "to suppose", "to wish", "to want", "to miss"], "Simplified": "\u60f3"}]}, "English": "to think"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/我:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u6211", "Dictionary": {"Entries": [{"Traditional": "\u6211", "Pinyin": "wo3", "Translations": ["I", "me", "my"], "Simplified": "\u6211"}]}, "English": "I"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/是:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u662f", "Dictionary": {"Entries": [{"Traditional": "\u662f", "Pinyin": "shi4", "Translations": ["is", "are", "am", "yes", "to be"], "Simplified": "\u662f"}]}, "English": "is"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/是的:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u662f\u7684", "Dictionary": {"Entries": [{"Traditional": "\u662f\u7684", "Pinyin": "shi4 de5", "Translations": ["yes"], "Simplified": "\u662f\u7684"}]}, "English": "yes"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/朋友:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u670b\u53cb", "Dictionary": {"Entries": [{"Traditional": "\u670b\u53cb", "Pinyin": "peng2 you5", "Translations": ["friend", "CL:\u500b|\u4e2a[ge4],\u4f4d[wei4]"], "Simplified": "\u670b\u53cb"}]}, "English": "friend"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/没:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u6ca1", "Dictionary": {"Entries": [{"Traditional": "\u6c92", "Pinyin": "mei2", "Translations": ["(negative prefix for verbs)", "have not", "not"], "Simplified": "\u6ca1"}]}, "English": "(negative prefix for verbs)"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/海:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u6d77", "Dictionary": {"Entries": [{"Traditional": "\u6d77", "Pinyin": "hai3", "Translations": ["ocean", "sea", "CL:\u500b|\u4e2a[ge4],\u7247[pian4]"], "Simplified": "\u6d77"}]}, "English": "sea"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/美国:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u7f8e\u56fd", "Dictionary": {"Entries": [{"Traditional": "\u7f8e\u570b", "Pinyin": "Mei3 guo2", "Translations": ["United States", "USA", "US"], "Simplified": "\u7f8e\u56fd"}]}, "English": "USA"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/老师:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u8001\u5e08", "Dictionary": {"Entries": [{"Traditional": "\u8001\u5e2b", "Pinyin": "lao3 shi1", "Translations": ["teacher", "CL:\u500b|\u4e2a[ge4],\u4f4d[wei4]"], "Simplified": "\u8001\u5e08"}]}, "English": "teacher"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/英国:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u82f1\u56fd", "Dictionary": {"Entries": [{"Traditional": "\u82f1\u570b", "Pinyin": "Ying1 guo2", "Translations": ["United Kingdom \u806f\u5408\u738b\u570b|\u8054\u5408\u738b\u56fd[Lian2 he2 wang2 guo2]", "United Kingdom of Great Britain and Northern Ireland", "abbr. for England \u82f1\u683c\u862d|\u82f1\u683c\u5170[Ying1 ge2 lan2]"], "Simplified": "\u82f1\u56fd"}]}, "English": "United Kingdom of Great Britain and Northern Ireland"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/认识:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u8ba4\u8bc6", "Dictionary": {"Entries": [{"Traditional": "\u8a8d\u8b58", "Pinyin": "ren4 shi5", "Translations": ["to know", "to recognize", "to be familiar with", "acquainted with sth", "knowledge", "understanding", "awareness", "cognition"], "Simplified": "\u8ba4\u8bc6"}]}, "English": "to know"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/谁:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u8c01", "Dictionary": {"Entries": [{"Traditional": "\u8ab0", "Pinyin": "shei2", "Translations": ["who", "also pronounced shui2"], "Simplified": "\u8c01"}]}, "English": "who"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/贵:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u8d35", "Dictionary": {"Entries": [{"Traditional": "\u8cb4", "Pinyin": "gui4", "Translations": ["expensive", "noble", "your (name)", "precious"], "Simplified": "\u8d35"}]}, "English": "your (name)"}
--------------------------------------------------------------------------------
/ch14/flashcardsets/Lesson04/都:
--------------------------------------------------------------------------------
1 | {"Simplified": "\u90fd", "Dictionary": {"Entries": [{"Traditional": "\u90fd", "Pinyin": "dou1", "Translations": ["all, both", "entirely (due to) each", "even", "already"], "Simplified": "\u90fd"}]}, "English": "all, both"}
--------------------------------------------------------------------------------
/ch14/server.go:
--------------------------------------------------------------------------------
1 | /* Server
2 | */
3 |
4 | package main
5 |
6 | import (
7 | "fmt"
8 | "html/template"
9 | "log"
10 | "net/http"
11 | "net/url"
12 | "os"
13 | "regexp"
14 | "strconv"
15 | "strings"
16 | )
17 |
18 | type FlashcardSet struct {
19 | Name string
20 | Link string
21 | }
22 |
23 | type Flashcard struct {
24 | Name string
25 | Link string
26 | }
27 |
28 | const flashcard_xml string = "application/x.flashcards+xml"
29 | const flashcard_json string = "application/x.flashcards+json"
30 |
31 | type ValueQuality struct {
32 | Value string
33 | Quality float64
34 | }
35 |
36 | /* Based on https://siongui.github.io/2015/02/22/go-parse-accept-language/ */
37 | func parseValueQuality(s string) []ValueQuality {
38 | var vqs []ValueQuality
39 |
40 | strs := strings.Split(s, `,`)
41 | for _, str := range strs {
42 | trimmedStr := strings.Trim(str, ` `)
43 | valQ := strings.Split(trimmedStr, `;`)
44 | if len(valQ) == 1 {
45 | vq := ValueQuality{valQ[0], 1}
46 | vqs = append(vqs, vq)
47 | } else {
48 | qp := strings.Split(valQ[1], `=`)
49 | q, err := strconv.ParseFloat(qp[1], 64)
50 | if err != nil {
51 | q = 0
52 | }
53 | vq := ValueQuality{valQ[0], q}
54 | vqs = append(vqs, vq)
55 | }
56 | }
57 | return vqs
58 | }
59 |
60 | func qualityOfValue(value string, vqs []ValueQuality) float64 {
61 | for _, vq := range vqs {
62 | if value == vq.Value {
63 | return vq.Quality
64 | }
65 | }
66 | return 0
67 | }
68 |
69 | func main() {
70 | if len(os.Args) != 2 {
71 | log.Fatalln("Usage: ", os.Args[0], ":port\n")
72 | }
73 | port := os.Args[1]
74 |
75 | http.HandleFunc(`/`, handleFlashCardSets)
76 | files, err := os.ReadDir(`flashcardsets`)
77 | checkError(err)
78 | for _, file := range files {
79 | fmt.Println(file.Name())
80 | cardset_url := `/flashcardSets/` + url.QueryEscape(file.Name())
81 | fmt.Println("Adding handlers for ", cardset_url)
82 | http.HandleFunc(cardset_url, handleOneFlashCardSet)
83 | http.HandleFunc(cardset_url+`/`, handleOneFlashCard)
84 | }
85 |
86 | // deliver requests to the handlers
87 | err = http.ListenAndServe(port, nil)
88 | checkError(err)
89 | }
90 |
91 | func hasIllegalChars(s string) bool {
92 | // check against chars to break out of current dir
93 | b, err := regexp.Match("[/$~]", []byte(s))
94 | if err != nil {
95 | fmt.Println(err)
96 | return true
97 | }
98 | if b {
99 | return true
100 | }
101 | return false
102 | }
103 |
104 | func handleOneFlashCard(rw http.ResponseWriter, req *http.Request) {
105 | // should be PathUnescape
106 | path, _ := url.QueryUnescape(req.URL.String())
107 | // lose intial '/'
108 | path = path[1:]
109 | if req.Method == http.MethodGet {
110 | fmt.Println("Handling card: ", path)
111 | json_contents, err := os.ReadFile(path)
112 | if err != nil {
113 | rw.WriteHeader(http.StatusNotFound)
114 | rw.Write([]byte(`Resource not found`))
115 | return
116 | }
117 | // Be lazy here, just return the content as text/plain
118 | rw.Write(json_contents)
119 | return
120 | } else if req.Method == http.MethodDelete {
121 | rw.WriteHeader(http.StatusNotImplemented)
122 | } else {
123 | rw.WriteHeader(http.StatusMethodNotAllowed)
124 | }
125 | return
126 | }
127 |
128 | func handleFlashCardSets(rw http.ResponseWriter, req *http.Request) {
129 | if req.URL.String() != `/` {
130 | // this function only handles '/'
131 | rw.WriteHeader(http.StatusNotFound)
132 | rw.Write([]byte("Resource not found\n"))
133 | return
134 | }
135 | if req.Method == "GET" {
136 | acceptTypes := parseValueQuality(req.Header.Get("Accept"))
137 | fmt.Println(acceptTypes)
138 |
139 | q_xml := qualityOfValue(flashcard_xml, acceptTypes)
140 | q_json := qualityOfValue(flashcard_json, acceptTypes)
141 | if q_xml == 0 && q_json == 0 {
142 | // can't find XML or JSON in Accept header
143 | rw.Header().Set("Content-Type", "flashcards+xml, flashcards+json")
144 | rw.WriteHeader(http.StatusNotAcceptable)
145 | return
146 | }
147 |
148 | files, err := os.ReadDir(`flashcardsets`)
149 | checkError(err)
150 | numfiles := len(files)
151 | cardSets := make([]FlashcardSet, numfiles, numfiles)
152 | for n, file := range files {
153 | fmt.Println(file.Name())
154 | cardSets[n].Name = file.Name()
155 | // should be PathEscape, not in go 1.6
156 | cardSets[n].Link = `/flashcardSets/` + url.QueryEscape(file.Name())
157 | }
158 | parseFile := "xml/ListFlashcardSets.xml"
159 | flashcardType := flashcard_xml
160 | if q_xml < q_json {
161 | parseFile = "json/ListFlashcardSets.json"
162 | flashcardType = flashcard_json
163 | }
164 | t, err := template.ParseFiles(parseFile)
165 | if err != nil {
166 | fmt.Println("Template error")
167 | http.Error(rw, err.Error(), http.StatusInternalServerError)
168 | return
169 | }
170 | rw.Header().Set("Content-Type", flashcardType)
171 | t.Execute(rw, cardSets)
172 | } else if req.Method == "POST" {
173 | name := req.FormValue(`name`)
174 | if hasIllegalChars(name) {
175 | rw.WriteHeader(http.StatusForbidden)
176 | return
177 | }
178 | // lose all spaces as they are a nuisance
179 | name = strings.Replace(name, ` `, ``, -1)
180 | err := os.Mkdir(`flashcardsets/`+name,
181 | (os.ModeDir | os.ModePerm))
182 | if err != nil {
183 | rw.WriteHeader(http.StatusForbidden)
184 | return
185 | }
186 | rw.WriteHeader(http.StatusCreated)
187 | base_url := req.URL.String()
188 | new_url := base_url + `flashcardSets/` + name + `/`
189 | rw.Write([]byte(new_url))
190 | } else {
191 | rw.WriteHeader(http.StatusMethodNotAllowed)
192 | }
193 | return
194 | }
195 |
196 | func handleOneFlashCardSet(rw http.ResponseWriter, req *http.Request) {
197 | cooked_url, _ := url.QueryUnescape(req.URL.String())
198 | fmt.Println("Handling one set for: ", cooked_url)
199 |
200 | if req.Method == http.MethodGet {
201 | acceptTypes := parseValueQuality(req.Header.Get("Accept"))
202 | fmt.Println(acceptTypes)
203 |
204 | q_xml := qualityOfValue(flashcard_xml, acceptTypes)
205 | q_json := qualityOfValue(flashcard_json, acceptTypes)
206 | if q_xml == 0 && q_json == 0 {
207 | // can't find XML or JSON in Accept header
208 | rw.Header().Set("Content-Type", "flashcards+xml, flashcards+json")
209 | rw.WriteHeader(http.StatusNotAcceptable)
210 | return
211 | }
212 |
213 | path := req.URL.String()
214 | // lose leading /
215 | relative_path := path[1:]
216 | files, err := os.ReadDir(relative_path)
217 | checkError(err)
218 | numfiles := len(files)
219 | cards := make([]Flashcard, numfiles, numfiles)
220 | for n, file := range files {
221 | fmt.Println(file.Name())
222 | cards[n].Name = file.Name()
223 | // should be PathEscape, not in go 1.6
224 | cards[n].Link = path + `/` + url.QueryEscape(file.Name())
225 | }
226 |
227 | if q_xml >= q_json {
228 | // XML preferred
229 | t, err := template.ParseFiles("xml/ListOneFlashcardSet.xml")
230 | if err != nil {
231 | fmt.Println("Template error")
232 | http.Error(rw, err.Error(), http.StatusInternalServerError)
233 | return
234 | }
235 | rw.Header().Set("Content-Type", flashcard_xml)
236 | t.Execute(os.Stdout, cards)
237 | t.Execute(rw, cards)
238 | } else {
239 | // JSON preferred
240 | t, err := template.ParseFiles("json/ListOneFlashcardSet.json")
241 | if err != nil {
242 | fmt.Println("Template error", err)
243 | http.Error(rw, err.Error(), http.StatusInternalServerError)
244 | return
245 | }
246 | rw.Header().Set("Content-Type", flashcard_json)
247 | t.Execute(rw, cards)
248 |
249 | }
250 | } else if req.Method == http.MethodPost {
251 | name := req.FormValue(`name`)
252 | if hasIllegalChars(name) {
253 | rw.WriteHeader(http.StatusForbidden)
254 | return
255 | }
256 | err := os.Mkdir(`flashcardsets/`+name,
257 | (os.ModeDir | os.ModePerm))
258 | if err != nil {
259 | rw.WriteHeader(http.StatusForbidden)
260 | return
261 | }
262 | rw.WriteHeader(http.StatusCreated)
263 | base_url := req.URL.String()
264 | new_url := base_url + `flashcardSets/` + name
265 | _, _ = rw.Write([]byte(new_url))
266 | } else if req.Method == http.MethodDelete {
267 | rw.WriteHeader(http.StatusNotImplemented)
268 | } else {
269 | rw.WriteHeader(http.StatusMethodNotAllowed)
270 | }
271 | return
272 | }
273 |
274 | func checkError(err error) {
275 | if err != nil {
276 | log.Fatalln("Fatal error ", err.Error())
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/ch14/xml/ListFlashcardSets.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{range .}}
5 |
6 |
7 | {{.Name}}
8 |
9 |
10 | {{end}}
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ch14/xml/ListOneFlashcardSet.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{range .}}
5 |
6 |
7 | {{.Name}}
8 |
9 |
10 | {{end}}
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ch15/echoclient.go:
--------------------------------------------------------------------------------
1 | /* EchoClient
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "golang.org/x/net/websocket"
8 | "io"
9 | "os"
10 | "log"
11 | )
12 |
13 | func main() {
14 | if len(os.Args) != 2 {
15 | log.Fatalln("Usage: ", os.Args[0], "ws://host:port")
16 | }
17 | service := os.Args[1]
18 | conn, err := websocket.Dial(service, "",
19 | "http://localhost:12345")
20 | checkError(err)
21 | var msg string
22 | for {
23 | err := websocket.Message.Receive(conn, &msg)
24 | if err != nil {
25 | if err == io.EOF {
26 | // graceful shutdown by server
27 | break
28 | }
29 | fmt.Println("Couldn't receive msg " +
30 | err.Error())
31 | break
32 | }
33 | fmt.Println("Received from server: " + msg)
34 | // return the msg
35 | err = websocket.Message.Send(conn, msg)
36 | if err != nil {
37 | fmt.Println("Couldn't return msg")
38 | break
39 | }
40 | }
41 | }
42 | func checkError(err error) {
43 | if err != nil {
44 | log.Fatalln("Fatal error ", err.Error())
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ch15/echoclientgorilla.go:
--------------------------------------------------------------------------------
1 | /* EchoClientGorilla
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "github.com/gorilla/websocket"
8 | "io"
9 | "log"
10 | "net/http"
11 | "os"
12 | )
13 |
14 | func main() {
15 | if len(os.Args) != 2 {
16 | log.Fatalln("Usage: ", os.Args[0], "ws://host:port")
17 | }
18 | service := os.Args[1]
19 | header := make(http.Header)
20 | header.Add("Origin", "http://localhost:12345")
21 | conn, _, err := websocket.DefaultDialer.Dial(service, header)
22 | checkError(err)
23 | for {
24 | _, reply, err := conn.ReadMessage()
25 | if err != nil {
26 | if err == io.EOF {
27 | // graceful shutdown by server
28 | fmt.Println(`EOF from server`)
29 | break
30 | }
31 | if websocket.IsCloseError(err,
32 | websocket.CloseAbnormalClosure) {
33 | fmt.Println(`Close from server`)
34 | break
35 | }
36 | fmt.Println("Couldn't receive msg " +
37 | err.Error())
38 | break
39 | }
40 | fmt.Println("Received from server: " +
41 | string(reply[:]))
42 | // return the msg
43 | err = conn.WriteMessage(websocket.TextMessage, reply)
44 | if err != nil {
45 | fmt.Println("Couldn't return msg")
46 | break
47 | }
48 | }
49 | }
50 | func checkError(err error) {
51 | if err != nil {
52 | log.Fatalln("Fatal error ", err.Error())
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ch15/echoclienttls.go:
--------------------------------------------------------------------------------
1 | /* EchoClientTLS
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/tls"
7 | "fmt"
8 | "golang.org/x/net/websocket"
9 | "io"
10 | "log"
11 | "os"
12 | )
13 |
14 | func main() {
15 | if len(os.Args) != 2 {
16 | log.Fatalln("Usage: ", os.Args[0], "wss://host:port")
17 | }
18 | config, err := websocket.NewConfig(os.Args[1],
19 | "http://localhost")
20 | checkError(err)
21 | tlsConfig := &tls.Config{InsecureSkipVerify: false}
22 | config.TlsConfig = tlsConfig
23 | conn, err := websocket.DialConfig(config)
24 | checkError(err)
25 | var msg string
26 | for {
27 | err := websocket.Message.Receive(conn, &msg)
28 | if err != nil {
29 | if err == io.EOF {
30 | // graceful shutdown by server
31 | break
32 | }
33 | fmt.Println("Couldn't receive msg " +
34 | err.Error())
35 | break
36 | }
37 | fmt.Println("Received from server: " + msg)
38 | // return the msg
39 | err = websocket.Message.Send(conn, msg)
40 | if err != nil {
41 | fmt.Println("Couldn't return msg")
42 | break
43 | }
44 | }
45 | }
46 | func checkError(err error) {
47 | if err != nil {
48 | log.Fatalln("Fatal error ", err.Error())
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ch15/echoserver.go:
--------------------------------------------------------------------------------
1 | /* EchoServer
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "golang.org/x/net/websocket"
8 | "log"
9 | "net/http"
10 | )
11 |
12 | func Echo(ws *websocket.Conn) {
13 | fmt.Println("Echoing")
14 | for n := 0; n < 10; n++ {
15 | msg := "Hello " + string(n+48)
16 | fmt.Println("Sending to client: " + msg)
17 | err := websocket.Message.Send(ws, msg)
18 | if err != nil {
19 | fmt.Println("Can't send")
20 | break
21 | }
22 | var reply string
23 | err = websocket.Message.Receive(ws, &reply)
24 | if err != nil {
25 | fmt.Println("Can't receive")
26 | break
27 | }
28 | fmt.Println("Received back from client: " + reply)
29 | }
30 | }
31 | func main() {
32 | http.Handle("/", websocket.Handler(Echo))
33 | err := http.ListenAndServe(":12345", nil)
34 | checkError(err)
35 | }
36 | func checkError(err error) {
37 | if err != nil {
38 | log.Fatalln("Fatal error ", err.Error())
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ch15/echoservergorilla.go:
--------------------------------------------------------------------------------
1 | /* EchoServerGorilla
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "github.com/gorilla/websocket"
8 | "log"
9 | "net/http"
10 | )
11 |
12 | var upgrader = websocket.Upgrader{
13 | ReadBufferSize: 1024,
14 | WriteBufferSize: 1024,
15 | }
16 |
17 | func Handler(w http.ResponseWriter, r *http.Request) {
18 | fmt.Println("Handling /")
19 | conn, err := upgrader.Upgrade(w, r, nil)
20 | if err != nil {
21 | fmt.Println(err)
22 | return
23 | }
24 | for n := 0; n < 10; n++ {
25 | msg := "Hello " + string(n+48)
26 | fmt.Println("Sending to client: " + msg)
27 | err = conn.WriteMessage(websocket.TextMessage,
28 | []byte(msg))
29 | _, reply, err := conn.ReadMessage()
30 | if err != nil {
31 | fmt.Println("Can't receive")
32 | break
33 | }
34 | fmt.Println("Received back from client: " +
35 | string(reply[:]))
36 | }
37 | conn.Close()
38 | }
39 | func main() {
40 | http.HandleFunc("/", Handler)
41 | err := http.ListenAndServe("localhost:12345", nil)
42 | checkError(err)
43 | }
44 | func checkError(err error) {
45 | if err != nil {
46 | log.Fatalln("Fatal error ", err.Error())
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ch15/echoservertls.go:
--------------------------------------------------------------------------------
1 | /* EchoServerTLS
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "fmt"
8 | "golang.org/x/net/websocket"
9 | "net/http"
10 | )
11 |
12 | func Echo(ws *websocket.Conn) {
13 | fmt.Println("Echoing")
14 | for n := 0; n < 10; n++ {
15 | msg := "Hello " + string(n+48)
16 | fmt.Println("Sending to client: " + msg)
17 | err := websocket.Message.Send(ws, msg)
18 | if err != nil {
19 | fmt.Println("Can't send")
20 | break
21 | }
22 | var reply string
23 | err = websocket.Message.Receive(ws, &reply)
24 | if err != nil {
25 | fmt.Println("Can't receive")
26 | break
27 | }
28 | fmt.Println("Received back from client: " + reply)
29 | }
30 | }
31 | func main() {
32 | http.Handle("/", websocket.Handler(Echo))
33 | err := http.ListenAndServeTLS(":12345",
34 | "jan.newmarch.name.pem",
35 | "private.pem", nil)
36 | checkError(err)
37 | }
38 | func checkError(err error) {
39 | if err != nil {
40 | log.Fatalln("Fatal error ", err.Error())
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ch15/personclientjson.go:
--------------------------------------------------------------------------------
1 | /* PersonClientJSON
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "golang.org/x/net/websocket"
8 | "log"
9 | "os"
10 | )
11 |
12 | type Person struct {
13 | Name string
14 | Emails []string
15 | }
16 |
17 | func main() {
18 | if len(os.Args) != 2 {
19 | log.Fatalln("Usage: ", os.Args[0], "ws://host:port")
20 | }
21 | service := os.Args[1]
22 | conn, err := websocket.Dial(service, "",
23 | "http://localhost")
24 | checkError(err)
25 | person := Person{Name: "Jan",
26 | Emails: []string{"ja@newmarch.name",
27 | "jan.newmarch@gmail.com"},
28 | }
29 | err = websocket.JSON.Send(conn, person)
30 | if err != nil {
31 | fmt.Println("Couldn't send msg " + err.Error())
32 | }
33 | }
34 | func checkError(err error) {
35 | if err != nil {
36 | log.Fatalln("Fatal error ", err.Error())
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ch15/personclientxml.go:
--------------------------------------------------------------------------------
1 | /* PersonClientXML
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "golang.org/x/net/websocket"
8 | "log"
9 | "os"
10 | )
11 |
12 | type Person struct {
13 | Name string
14 | Emails []string
15 | }
16 |
17 | func main() {
18 | if len(os.Args) != 2 {
19 | log.Fatalln("Usage: ", os.Args[0], "ws://host:port")
20 | }
21 | service := os.Args[1]
22 | conn, err := websocket.Dial(service, "", "http://localhost")
23 | checkError(err)
24 | person := Person{Name: "Jan",
25 | Emails: []string{"ja@newmarch.name",
26 | "jan.newmarch@gmail.com"},
27 | }
28 | err = XMLCodec.Send(conn, person)
29 | if err != nil {
30 | fmt.Println("Couldn't send msg " + err.Error())
31 | }
32 | os.Exit(0)
33 | }
34 | func checkError(err error) {
35 | if err != nil {
36 | log.Fatalln("Fatal error ", err.Error())
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ch15/personserverjson.go:
--------------------------------------------------------------------------------
1 | /* PersonServerJSON
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "golang.org/x/net/websocket"
8 | "log"
9 | "net/http"
10 | )
11 |
12 | type Person struct {
13 | Name string
14 | Emails []string
15 | }
16 |
17 | func ReceivePerson(ws *websocket.Conn) {
18 | var person Person
19 | err := websocket.JSON.Receive(ws, &person)
20 | if err != nil {
21 | fmt.Println("Can't receive")
22 | } else {
23 | fmt.Println("Name: " + person.Name)
24 | for _, e := range person.Emails {
25 | fmt.Println("An email: " + e)
26 | }
27 | }
28 | }
29 | func main() {
30 | http.Handle("/", websocket.Handler(ReceivePerson))
31 | err := http.ListenAndServe(":12345", nil)
32 | checkError(err)
33 | }
34 | func checkError(err error) {
35 | if err != nil {
36 | log.Fatalln("Fatal error ", err.Error())
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ch15/personserverxml.go:
--------------------------------------------------------------------------------
1 | /* PersonServerXML
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "golang.org/x/net/websocket"
8 | "log"
9 | "net/http"
10 | )
11 |
12 | type Person struct {
13 | Name string
14 | Emails []string
15 | }
16 |
17 | func ReceivePerson(ws *websocket.Conn) {
18 | var person Person
19 | err := XMLCodec.Receive(ws, &person)
20 | if err != nil {
21 | fmt.Println("Can't receive")
22 | } else {
23 | fmt.Println("Name: " + person.Name)
24 | for _, e := range person.Emails {
25 | fmt.Println("An email: " + e)
26 | }
27 | }
28 | }
29 | func main() {
30 | http.Handle("/", websocket.Handler(ReceivePerson))
31 | err := http.ListenAndServe(":12345", nil)
32 | checkError(err)
33 | }
34 | func checkError(err error) {
35 | if err != nil {
36 | log.Fatalln("Fatal error ", err.Error())
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ch15/sensors.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | echo $(( ( RANDOM % 100 ) + 1 ))
3 |
--------------------------------------------------------------------------------
/ch15/temperatureserver.go:
--------------------------------------------------------------------------------
1 | /* TemperatureServer
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "golang.org/x/net/websocket"
8 | "log"
9 | "net/http"
10 | "os/exec"
11 | "time"
12 | )
13 |
14 | var ROOT_DIR = "."
15 |
16 | func GetTemp(ws *websocket.Conn) {
17 | for {
18 | msg, err := exec.Command(ROOT_DIR + "/sensors.sh").CombinedOutput()
19 | checkError(err)
20 | fmt.Println("Sending to client: " + string(msg[:]))
21 | err = websocket.Message.Send(ws, string(msg[:]))
22 | if err != nil {
23 | fmt.Println("Can't send")
24 | break
25 | }
26 | time.Sleep(time.Duration(2) * time.Second)
27 | var reply string
28 | err = websocket.Message.Receive(ws, &reply)
29 | if err != nil {
30 | fmt.Println("Can't receive")
31 | break
32 | }
33 | fmt.Println("Received back from client: " + reply)
34 | }
35 | }
36 | func main() {
37 | fileServer := http.FileServer(http.Dir(ROOT_DIR))
38 | http.Handle("/GetTemp", websocket.Handler(GetTemp))
39 | http.Handle("/", fileServer)
40 | err := http.ListenAndServe(":12345", nil)
41 | checkError(err)
42 | }
43 | func checkError(err error) {
44 | if err != nil {
45 | log.Fatalln("Fatal error ", err.Error())
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/ch15/websocket.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
38 |
39 |
40 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ch15/xmlcodec.go:
--------------------------------------------------------------------------------
1 | /* XMLCodec
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/xml"
7 | "golang.org/x/net/websocket"
8 | )
9 |
10 | func xmlMarshal(v interface{}) (msg []byte, payloadType byte, err error) {
11 | msg, err = xml.Marshal(v)
12 | return msg, websocket.TextFrame, nil
13 | }
14 | func xmlUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
15 | err = xml.Unmarshal(msg, v)
16 | return err
17 | }
18 |
19 | var XMLCodec = websocket.Codec{xmlMarshal, xmlUnmarshal}
20 |
--------------------------------------------------------------------------------
/ch16/contenttype.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gorilla/handlers"
5 | "github.com/gorilla/mux"
6 | "net/http"
7 | )
8 |
9 | func buildHandler(message string) func(http.ResponseWriter, *http.Request) {
10 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
11 | w.Write([]byte(message))
12 | })
13 | }
14 |
15 | func main() {
16 | r := mux.NewRouter()
17 | r.HandleFunc("/", buildHandler("HomeHandler"))
18 | r.HandleFunc("/products", buildHandler("ProductsHandler"))
19 | l := handlers.ContentTypeHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
20 | w.Write([]byte("json articles only"))
21 | }), "application/json")
22 | r.Handle("/articles", l)
23 | http.ListenAndServe(":8080", r)
24 | }
25 |
--------------------------------------------------------------------------------
/ch16/gmux.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gorilla/mux"
5 | "net/http"
6 | )
7 |
8 | func buildHandler(message string) func(http.ResponseWriter, *http.Request) {
9 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
10 | w.Write([]byte(message))
11 | })
12 | }
13 |
14 | func main() {
15 | r := mux.NewRouter()
16 | r.HandleFunc("/", buildHandler("HomeHandler"))
17 | r.HandleFunc("/products", buildHandler("ProductsHandler"))
18 | r.HandleFunc("/articles", buildHandler("ArticlesHandler"))
19 | http.ListenAndServe(":8080", r)
20 | }
21 |
--------------------------------------------------------------------------------
/ch16/logging.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gorilla/mux"
5 | "github.com/gorilla/handlers"
6 | "os"
7 | "net/http"
8 | )
9 |
10 | func buildHandler(message string) func(http.ResponseWriter, *http.Request) {
11 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
12 | w.Write([]byte(message))
13 | })
14 | }
15 |
16 | func main() {
17 | r := mux.NewRouter()
18 | r.HandleFunc("/", buildHandler("HomeHandler"))
19 | r.HandleFunc("/products", buildHandler("ProductsHandler"))
20 | r.HandleFunc("/articles", buildHandler("ArticlesHandler")).Host("example.com").Methods("GET").Schemes("http")
21 | loggedRouter := handlers.LoggingHandler(os.Stdout, r)
22 | http.ListenAndServe(":8080", loggedRouter)
23 | }
24 |
--------------------------------------------------------------------------------
/ch16/matching.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gorilla/mux"
5 | "net/http"
6 | )
7 |
8 | func buildHandler(message string) func(http.ResponseWriter, *http.Request) {
9 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
10 | w.Write([]byte(message))
11 | })
12 | }
13 |
14 | func main() {
15 | r := mux.NewRouter()
16 | r.HandleFunc("/", buildHandler("HomeHandler"))
17 | r.HandleFunc("/products", buildHandler("ProductsHandler"))
18 | r.HandleFunc("/articles", buildHandler("ArticlesHandler")).Host("example.com").Methods("GET").Schemes("http")
19 | http.ListenAndServe(":8080", nil)
20 | }
21 |
--------------------------------------------------------------------------------
/ch16/rpc.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gorilla/rpc"
5 | "github.com/gorilla/rpc/json"
6 | "net/http"
7 | )
8 |
9 | type HelloArgs struct {
10 | Who string
11 | }
12 |
13 | type HelloReply struct {
14 | Message string
15 | }
16 |
17 | type HelloService struct{}
18 |
19 | func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error {
20 | reply.Message = "Hello, " + args.Who + "!"
21 | return nil
22 | }
23 |
24 | func main() {
25 | s := rpc.NewServer()
26 | s.RegisterCodec(json.NewCodec(), "application/json")
27 | s.RegisterService(new(HelloService), "")
28 | http.Handle("/rpc", s)
29 | http.ListenAndServe(":8080", nil)
30 | }
31 |
--------------------------------------------------------------------------------
/ch16/schema.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/gorilla/schema"
6 | "net/http"
7 | )
8 |
9 | var decoder = schema.NewDecoder()
10 |
11 | type Person struct {
12 | Name string
13 | Phone string
14 | }
15 |
16 | func main() {
17 | http.HandleFunc("/schema", func(res http.ResponseWriter, req *http.Request) {
18 | req.ParseMultipartForm(0)
19 | var person Person
20 | decoder.Decode(&person, req.PostForm)
21 | message := fmt.Sprintf("Hello %v from area %v", person.Name, person.Phone)
22 | fmt.Printf("%v", req.PostForm)
23 | res.Write([]byte(message))
24 | })
25 |
26 | http.ListenAndServe(":8080", nil)
27 | }
28 |
--------------------------------------------------------------------------------
/ch16/securecookie.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/gorilla/securecookie"
6 | "net/http"
7 | )
8 |
9 | var hashKey = []byte("very-secret")
10 | var s = securecookie.New(hashKey, nil)
11 |
12 | func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
13 | value := map[string]string{
14 | "foo": "bar",
15 | }
16 | if encoded, err := s.Encode("cookie-name", value); err == nil {
17 | cookie := &http.Cookie{
18 | Name: "cookie-name",
19 | Value: encoded,
20 | Path: "/",
21 | }
22 | http.SetCookie(w, cookie)
23 |
24 | fmt.Println(cookie)
25 | }
26 | }
27 |
28 | func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
29 | if cookie, err := r.Cookie("cookie-name"); err == nil {
30 | value := make(map[string]string)
31 | fmt.Println(cookie.Value)
32 | if err = s.Decode("cookie-name", cookie.Value, &value); err == nil {
33 | fmt.Println("im ok!")
34 | fmt.Fprintf(w, "The value of foo is %q", value["foo"])
35 | }
36 |
37 | fmt.Println(cookie, err)
38 | }
39 |
40 | }
41 |
42 | func main() {
43 | http.HandleFunc("/set", SetCookieHandler)
44 | http.HandleFunc("/read", ReadCookieHandler)
45 | http.ListenAndServe(":8080", nil)
46 | }
47 |
--------------------------------------------------------------------------------
/ch17/basic_http_test.go:
--------------------------------------------------------------------------------
1 | package ch17
2 |
3 | import (
4 | "net"
5 | "net/http"
6 | "net/http/httptest"
7 | "testing"
8 | "time"
9 | )
10 |
11 | func TestHTTPRoundTripNoConnection(t *testing.T) {
12 | path := "/"
13 | req := httptest.NewRequest("GET", path, nil)
14 | res := httptest.NewRecorder()
15 |
16 | f := func(w http.ResponseWriter, req *http.Request) {
17 | time.Sleep(5 * time.Second) // holding connection
18 | }
19 |
20 | f(res, req)
21 |
22 | if res == nil || res.Result().StatusCode != http.StatusOK {
23 | t.Error(res)
24 | }
25 | }
26 |
27 | func TestHTTPRoundTrip(t *testing.T) {
28 | path := "/"
29 | c := make(chan struct{})
30 | //server
31 | go func() {
32 | http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
33 | time.Sleep(5 * time.Second) // holding connection
34 | })
35 | http.ListenAndServe(":8080", nil)
36 | }()
37 |
38 | //client
39 | go func() {
40 | resp, err := http.Get("http://localhost:8080" + path)
41 | if err != nil {
42 | t.Error(err)
43 | } else {
44 | if resp == nil || resp.StatusCode != http.StatusOK {
45 | t.Error(resp)
46 | }
47 | }
48 |
49 | defer func() {
50 | c <- struct{}{}
51 | }()
52 | }()
53 | <-c
54 | }
55 |
56 | func TestHTTPTestRoundTripTimeout(t *testing.T) {
57 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
58 | time.Sleep(5 * time.Second) // holding connection
59 | }))
60 | defer ts.Close()
61 |
62 | var c = &http.Client{
63 | Timeout: time.Second * 2,
64 | }
65 | req, _ := http.NewRequest("GET", ts.URL, nil)
66 | res, err := c.Do(req)
67 | if err != nil {
68 | t.Fatal(err)
69 | }
70 |
71 | err = res.Body.Close()
72 | if err != nil {
73 | t.Fatal(err)
74 | }
75 |
76 | }
77 |
78 | func TestHTTPRoundTripTimeout(t *testing.T) {
79 | path := "/unique"
80 | c := make(chan struct{})
81 | //server
82 | go func() {
83 | http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
84 | time.Sleep(5 * time.Second) // holding connection
85 | })
86 | http.ListenAndServe(":8080", nil)
87 | }()
88 |
89 | //client
90 | go func() {
91 | var client = &http.Client{
92 | Timeout: time.Second * 2,
93 | }
94 | resp, err := client.Get("http://localhost:8080" + path)
95 | if err != nil {
96 | t.Error(err)
97 | } else {
98 | if resp == nil || resp.StatusCode != http.StatusOK {
99 | t.Error(resp)
100 | }
101 | }
102 |
103 | defer func() {
104 | c <- struct{}{}
105 | }()
106 | }()
107 | <-c
108 | }
109 |
110 | func TestPipe(t *testing.T) {
111 | c := make(chan struct{})
112 | server, client := net.Pipe()
113 | go func() {
114 | time.Sleep(2 * time.Second)
115 | req := make([]byte, 15)
116 | server.SetDeadline(time.Now().Add(1 * time.Second))
117 | _, err := server.Read(req)
118 | t.Log(string(req))
119 | if err != nil {
120 | t.Error(err)
121 | }
122 | defer func() {
123 | server.Close()
124 | c <- struct{}{}
125 | }()
126 | }()
127 | client.SetDeadline(time.Now().Add(1 * time.Second))
128 | _, err := client.Write([]byte("my http request"))
129 | if err != nil {
130 | t.Error(err)
131 | }
132 | defer func() {
133 | client.Close()
134 | }()
135 | <-c
136 | }
137 |
--------------------------------------------------------------------------------
/ch3/daytimeserver.go:
--------------------------------------------------------------------------------
1 | /* DaytimeServer
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "net"
8 | "time"
9 | )
10 |
11 | func main() {
12 | service := ":1200"
13 | tcpAddr, err := net.ResolveTCPAddr("tcp", service)
14 | checkError(err)
15 | listener, err := net.ListenTCP("tcp", tcpAddr)
16 | checkError(err)
17 | for {
18 | conn, err := listener.Accept()
19 | if err != nil {
20 | continue
21 | }
22 | daytime := time.Now().String()
23 | conn.Write([]byte(daytime)) // don't care about return value
24 | conn.Close() // we're finished with this client
25 | }
26 | }
27 | func checkError(err error) {
28 | if err != nil {
29 | log.Fatalln("Fatal error: %s", err.Error())
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ch3/getheadinfo.go:
--------------------------------------------------------------------------------
1 | /* GetHeadInfo
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "io/ioutil"
8 | "log"
9 | "net"
10 | "os"
11 | )
12 |
13 | func main() {
14 | if len(os.Args) != 2 {
15 | log.Fatalf("Usage: %s host:port ", os.Args[0])
16 | }
17 | service := os.Args[1]
18 | tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
19 | checkError(err)
20 | conn, err := net.DialTCP("tcp", nil, tcpAddr)
21 | checkError(err)
22 | _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
23 | checkError(err)
24 | result, err := ioutil.ReadAll(conn)
25 | checkError(err)
26 | fmt.Println(string(result))
27 | }
28 | func checkError(err error) {
29 | if err != nil {
30 | log.Fatalf("Fatal error: %s", err.Error())
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ch3/ip.go:
--------------------------------------------------------------------------------
1 | /* IP
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | "os"
10 | )
11 |
12 | func main() {
13 | if len(os.Args) != 2 {
14 | log.Fatalf("Usage: %s ip-addr\n", os.Args[0])
15 | }
16 | name := os.Args[1]
17 | addr := net.ParseIP(name)
18 | if addr == nil {
19 | fmt.Println("Invalid address")
20 | } else {
21 | fmt.Println("The address is ", addr.String())
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ch3/ipgetheadinfo.go:
--------------------------------------------------------------------------------
1 | /* IPGetHeadInfo
2 | */
3 | package main
4 |
5 | import (
6 | "bytes"
7 | "fmt"
8 | "io"
9 | "log"
10 | "net"
11 | "os"
12 | )
13 |
14 | func main() {
15 | if len(os.Args) != 2 {
16 | log.Fatalf("Usage: %s host:port", os.Args[0])
17 | }
18 | service := os.Args[1]
19 | conn, err := net.Dial("tcp", service)
20 | checkError(err)
21 | _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
22 | checkError(err)
23 | result, err := readFully(conn)
24 | checkError(err)
25 | fmt.Println(string(result))
26 | }
27 | func readFully(conn net.Conn) ([]byte, error) {
28 | defer conn.Close()
29 | result := bytes.NewBuffer(nil)
30 | var buf [512]byte
31 | for {
32 | n, err := conn.Read(buf[0:])
33 | result.Write(buf[0:n])
34 | if err != nil {
35 | if err == io.EOF {
36 | break
37 | }
38 | return nil, err
39 | }
40 | }
41 | return result.Bytes(), nil
42 | }
43 | func checkError(err error) {
44 | if err != nil {
45 | log.Fatalf("Fatal error: %s", err.Error())
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/ch3/ipv4mask.go:
--------------------------------------------------------------------------------
1 | /* IPv4Mask
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | "os"
10 | )
11 |
12 | func main() {
13 | if len(os.Args) != 2 {
14 | log.Fatalf("Usage: %s dotted-ip-addr\n", os.Args[0])
15 | }
16 | dotAddr := os.Args[1]
17 | addr := net.ParseIP(dotAddr)
18 | if addr == nil {
19 | log.Fatalln("nil Invalid address")
20 | }
21 | mask := addr.DefaultMask()
22 | network := addr.Mask(mask)
23 | ones, bits := mask.Size()
24 | fmt.Println("Address is ", addr.String(),
25 | "\nDefault mask length is ", bits,
26 | "\nLeading ones count is ", ones,
27 | "\nMask is (hex) ", mask.String(),
28 | "\nNetwork is ", network.String())
29 | derivedMask := net.IPv4Mask(255, 255, 0, 0) // working on mask
30 | fmt.Printf("Network using %s: %s\n", derivedMask, addr.Mask(derivedMask))
31 | }
32 |
--------------------------------------------------------------------------------
/ch3/ipv4router.go:
--------------------------------------------------------------------------------
1 | /* IPv4Router
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "net"
8 | )
9 |
10 | func main() {
11 | routingTable := []struct {
12 | subnetmask net.IP
13 | network net.IP
14 | nextHop net.IP
15 | }{
16 | {net.IP{255, 255, 255, 240}, net.IP{192, 17, 7, 208}, net.IP{192, 12, 7, 15}},
17 | {net.IP{255, 255, 255, 240}, net.IP{192, 17, 7, 144}, net.IP{192, 12, 7, 67}},
18 | {net.IP{255, 255, 255, 0}, net.IP{192, 17, 7, 0}, net.IP{192, 12, 7, 251}},
19 | {net.IP{0, 0, 0, 0}, net.IP{0, 0, 0, 0}, net.IP{10, 10, 10, 10}},
20 | }
21 | incomingPacketsToRoute := []struct {
22 | sourceAddr net.IP
23 | destinationAddr net.IP
24 | data string
25 | }{
26 | {net.IP{1, 2, 3, 4}, net.IP{2, 3, 4, 5}, "who knows, send to 0.0.0.0"},
27 | {net.IP{1, 2, 3, 4}, net.IP{192, 17, 7, 20}, "better be 192.17.7.251"},
28 | }
29 | for _, packetToRoute := range incomingPacketsToRoute {
30 | for _, routingEntry := range routingTable {
31 | r := packetToRoute.destinationAddr.Mask(net.IPMask(routingEntry.subnetmask))
32 | if r.Equal(routingEntry.network) {
33 | fmt.Printf("For destination %s nexthop is %s\n", packetToRoute.destinationAddr, routingEntry.nextHop)
34 | break //check remaining ips
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ch3/lookuphost.go:
--------------------------------------------------------------------------------
1 | /* LookupHost
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | "os"
10 | )
11 |
12 | func main() {
13 | if len(os.Args) != 2 {
14 | log.Fatalf("Usage: %s hostname\n", os.Args[0])
15 | }
16 | name := os.Args[1]
17 | cname, _ := net.LookupCNAME(name)
18 | fmt.Println(cname)
19 | addrs, err := net.LookupHost(cname)
20 | if err != nil {
21 | log.Fatalln("Error: ", err.Error())
22 | }
23 | for _, addr := range addrs {
24 | fmt.Println(addr)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ch3/lookupport.go:
--------------------------------------------------------------------------------
1 | /* LookupPort
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | "os"
10 | )
11 |
12 | func main() {
13 | if len(os.Args) != 3 {
14 | log.Fatalf("Usage: %s network-type service\n", os.Args[0])
15 | }
16 | networkType := os.Args[1]
17 | service := os.Args[2]
18 | port, err := net.LookupPort(networkType, service)
19 | if err != nil {
20 | log.Fatalln("Error: ", err.Error())
21 | }
22 | fmt.Println("Service port ", port)
23 | }
24 |
--------------------------------------------------------------------------------
/ch3/mask.go:
--------------------------------------------------------------------------------
1 | /* Mask
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | "os"
10 | "strconv"
11 | )
12 |
13 | func main() {
14 | if len(os.Args) != 4 {
15 | log.Fatalf("Usage: %s dotted-ip-addr ones bits\n", os.Args[0])
16 | }
17 | dotAddr := os.Args[1]
18 | ones, _ := strconv.Atoi(os.Args[2])
19 | bits, _ := strconv.Atoi(os.Args[3])
20 | addr := net.ParseIP(dotAddr)
21 | if addr == nil {
22 | log.Fatalln("nil Invalid address")
23 | }
24 | mask := net.CIDRMask(ones, bits)
25 | computedOnes, computedBits := mask.Size()
26 | network := addr.Mask(mask)
27 | fmt.Println("Address is ", addr.String(),
28 | "\nMask length is ", computedBits,
29 | "\nLeading ones count is ", computedOnes,
30 | "\nMask is (hex) ", mask.String(),
31 | "\nNetwork is ", network.String())
32 | }
33 |
--------------------------------------------------------------------------------
/ch3/ping.go:
--------------------------------------------------------------------------------
1 | /* Ping
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | "os"
10 | )
11 |
12 | // change this to your own IP address or leave set to 0.0.0.0
13 | const myIPAddress = "0.0.0.0"
14 | const ipv4HeaderSize = 20
15 |
16 | func main() {
17 | if len(os.Args) != 2 {
18 | log.Fatalln("Usage: ", os.Args[0], "host")
19 | }
20 | localAddr, err := net.ResolveIPAddr("ip4", myIPAddress)
21 | checkError(err)
22 |
23 | remoteAddr, err := net.ResolveIPAddr("ip4", os.Args[1])
24 | checkError(err)
25 |
26 | conn, err := net.DialIP("ip4:icmp", localAddr, remoteAddr)
27 | checkError(err)
28 |
29 | var msg [512]byte
30 | msg[0] = 8 // echo
31 | msg[1] = 0 // code 0
32 | msg[2] = 0 // checksum, fix later
33 | msg[3] = 0 // checksum, fix later
34 | msg[4] = 0 // identifier[0]
35 | msg[5] = 13 // identifier[1] (arbitrary)
36 | msg[6] = 0 // sequence[0]
37 | msg[7] = 37 // sequence[1] (arbitrary)
38 | len := 8
39 |
40 | // now fix checksum bytes
41 | check := checkSum(msg[0:len])
42 | msg[2] = byte(check >> 8)
43 | msg[3] = byte(check & 255)
44 |
45 | // send the message
46 | _, err = conn.Write(msg[0:len])
47 | checkError(err)
48 | fmt.Print("Message sent: ")
49 | for n := 0; n < 8; n++ {
50 | fmt.Print(" ", msg[n])
51 | }
52 | fmt.Println()
53 |
54 | // receive a reply
55 | size, err2 := conn.Read(msg[0:])
56 | checkError(err2)
57 | fmt.Print("Message received:")
58 | for n := ipv4HeaderSize; n < size; n++ {
59 | fmt.Print(" ", msg[n])
60 | }
61 | fmt.Println()
62 | os.Exit(0)
63 | }
64 | func checkSum(msg []byte) uint16 {
65 | sum := 0
66 | // assume even for now
67 | for n := 0; n < len(msg); n += 2 {
68 | sum += int(msg[n])*256 + int(msg[n+1])
69 | }
70 | sum = (sum >> 16) + (sum & 0xffff)
71 | sum += (sum >> 16)
72 | var answer uint16 = uint16(^sum)
73 | return answer
74 | }
75 | func checkError(err error) {
76 | if err != nil {
77 | log.Fatalln("Fatal error: %s", err.Error())
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/ch3/resolveip.go:
--------------------------------------------------------------------------------
1 | /* ResolveIP
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | "os"
10 | )
11 |
12 | func main() {
13 | if len(os.Args) != 2 {
14 | log.Fatalf("Usage: %s hostname\n", os.Args[0])
15 | }
16 | name := os.Args[1]
17 | addr, err := net.ResolveIPAddr("ip", name)
18 | if err != nil {
19 | log.Fatalln("Resolution error", err.Error())
20 | }
21 | fmt.Println("Resolved address is ", addr.String())
22 | }
23 |
--------------------------------------------------------------------------------
/ch3/simpleechoserver.go:
--------------------------------------------------------------------------------
1 | /* SimpleEchoServer
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | )
10 |
11 | func main() {
12 | service := ":1201"
13 | tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
14 | checkError(err)
15 | listener, err := net.ListenTCP("tcp", tcpAddr)
16 | checkError(err)
17 | for {
18 | conn, err := listener.Accept()
19 | if err != nil {
20 | continue
21 | }
22 | handleClient(conn)
23 | conn.Close() // we're finished
24 | }
25 | }
26 | func handleClient(conn net.Conn) {
27 | var buf [512]byte
28 | for {
29 | n, err := conn.Read(buf[0:])
30 | checkError(err)
31 | fmt.Println(string(buf[0:]))
32 | _, err = conn.Write(buf[0:n])
33 | checkError(err)
34 | }
35 | }
36 | func checkError(err error) {
37 | if err != nil {
38 | log.Fatalln("Fatal error: %s", err.Error())
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ch3/threadedechoserver.go:
--------------------------------------------------------------------------------
1 | /* ThreadedEchoServer
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | )
10 |
11 | func main() {
12 | service := ":1201"
13 | tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
14 | checkError(err)
15 | listener, err := net.ListenTCP("tcp", tcpAddr)
16 | checkError(err)
17 | for {
18 | conn, err := listener.Accept()
19 | if err != nil {
20 | continue
21 | }
22 | // run as a goroutine
23 | go handleClient(conn)
24 | }
25 | }
26 | func handleClient(conn net.Conn) {
27 | // close connection on exit
28 | defer conn.Close()
29 | var buf [512]byte
30 | for {
31 | // read up to 512 bytes
32 | n, err := conn.Read(buf[0:])
33 | checkError(err)
34 | fmt.Println(string(buf[0:]))
35 | // write the n bytes read
36 | _, err = conn.Write(buf[0:n])
37 | checkError(err)
38 | }
39 | }
40 | func checkError(err error) {
41 | if err != nil {
42 | log.Fatalf("Fatal error: %s", err.Error())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ch3/threadedipechoserver.go:
--------------------------------------------------------------------------------
1 | /* ThreadedIPEchoServer
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "net"
8 | )
9 |
10 | func main() {
11 | service := ":1200"
12 | listener, err := net.Listen("tcp", service)
13 | checkError(err)
14 | for {
15 | conn, err := listener.Accept()
16 | if err != nil {
17 | continue
18 | }
19 | go handleClient(conn)
20 | }
21 | }
22 | func handleClient(conn net.Conn) {
23 | defer conn.Close()
24 | var buf [512]byte
25 | for {
26 | n, err := conn.Read(buf[0:])
27 | checkError(err)
28 |
29 | _, err = conn.Write(buf[0:n])
30 | checkError(err)
31 | }
32 | }
33 | func checkError(err error) {
34 | if err != nil {
35 | log.Fatalf("Fatal error: %s", err.Error())
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/ch3/udpdaytimeclient.go:
--------------------------------------------------------------------------------
1 | /* UDPDaytimeClient
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | "os"
10 | )
11 |
12 | func main() {
13 | if len(os.Args) != 2 {
14 | log.Fatalf("Usage: %s host:port", os.Args[0])
15 | }
16 | service := os.Args[1]
17 | udpAddr, err := net.ResolveUDPAddr("udp", service)
18 | checkError(err)
19 | conn, err := net.DialUDP("udp", nil, udpAddr)
20 | checkError(err)
21 | _, err = conn.Write([]byte("anything"))
22 | checkError(err)
23 | var buf [512]byte
24 | n, err := conn.Read(buf[0:])
25 | checkError(err)
26 | fmt.Println(string(buf[0:n]))
27 | }
28 | func checkError(err error) {
29 | if err != nil {
30 | log.Fatalln("Fatal error ", err.Error())
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ch3/udpdaytimeserver.go:
--------------------------------------------------------------------------------
1 | /* UDPDaytimeServer
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "net"
8 | "time"
9 | )
10 |
11 | func main() {
12 | service := ":1200"
13 | udpAddr, err := net.ResolveUDPAddr("udp", service)
14 | checkError(err)
15 | conn, err := net.ListenUDP("udp", udpAddr)
16 | checkError(err)
17 | for {
18 | handleClient(conn)
19 | }
20 | }
21 | func handleClient(conn *net.UDPConn) {
22 | var buf [512]byte
23 | _, addr, err := conn.ReadFromUDP(buf[0:])
24 | checkError(err)
25 | daytime := time.Now().String()
26 | conn.WriteToUDP([]byte(daytime), addr)
27 | }
28 | func checkError(err error) {
29 | if err != nil {
30 | log.Fatalln("Fatal error ", err.Error())
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ch4/asn1.go:
--------------------------------------------------------------------------------
1 | /*
2 | ASN1 example.
3 | */
4 | package main
5 |
6 | import (
7 | "encoding/asn1"
8 | "fmt"
9 | "time"
10 | )
11 |
12 | func thirteen() {
13 | val := 13
14 | mdata, _ := asn1.Marshal(val)
15 | var n int
16 | _, _ = asn1.Unmarshal(mdata, &n)
17 | fmt.Printf("Before marshal: %v, After unmarshal: %v\n", val, n)
18 | }
19 |
20 | func ascii() {
21 | s := "hello"
22 | mdata, _ := asn1.Marshal(s)
23 | var newstr string
24 | _, _ = asn1.Unmarshal(mdata, &newstr)
25 | fmt.Printf("Before marshal: %v, After unmarshal: %v\n", s, newstr)
26 | }
27 |
28 | func myTime() {
29 | t := time.Now()
30 | mdata, _ := asn1.Marshal(t)
31 | var newtime = new(time.Time)
32 | _, _ = asn1.Unmarshal(mdata, newtime)
33 | fmt.Printf("Before marshal: %v, After unmarshal: %v\n", t, newtime)
34 | }
35 |
36 | func main() {
37 | thirteen()
38 | ascii()
39 | myTime()
40 | }
41 |
--------------------------------------------------------------------------------
/ch4/asn1basic.go:
--------------------------------------------------------------------------------
1 | /* ASN.1 Basic
2 | */
3 |
4 | package main
5 |
6 | import (
7 | "encoding/asn1"
8 | "fmt"
9 | "time"
10 | )
11 |
12 | func main() {
13 | // time pointer to time value
14 | t := time.Now()
15 | fmt.Println("Before marshalling: ", t.String())
16 | mdata, _ := asn1.Marshal(t)
17 | var newtime = new(time.Time)
18 | asn1.Unmarshal(mdata, newtime)
19 | fmt.Println("After marshal/unmarshal: ", newtime.String())
20 |
21 | // vulgar fraction, string to string
22 | s := "hello \u00bc"
23 | fmt.Println("Before marshalling: ", s)
24 | mdata2, _ := asn1.Marshal(s)
25 | var newstr string
26 | asn1.Unmarshal(mdata2, &newstr)
27 | fmt.Println("After marshal/unmarshal: ", newstr)
28 | }
29 |
--------------------------------------------------------------------------------
/ch4/asn1fields.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/asn1"
5 | "fmt"
6 | "log"
7 | )
8 |
9 | type MyType struct {
10 | F1 rune
11 | F2 int
12 | }
13 |
14 | type YourType struct {
15 | F3 rune
16 | }
17 |
18 | type TheirType struct {
19 | F4 byte
20 | }
21 |
22 | func main() {
23 | // this first example works
24 | t1 := MyType{'ロ', 1}
25 | mdata1, _ := asn1.Marshal(t1)
26 |
27 | t2 := new(YourType)
28 | _, err := asn1.Unmarshal(mdata1, t2)
29 | fmt.Printf("Before marshal: %v, after unmarshal: %v\n", t1, t2)
30 | checkError(err)
31 |
32 | // syntax error (fails to fill all fields)
33 | y := YourType{'ロ'}
34 | mdata2, _ := asn1.Marshal(y)
35 | z := new(MyType)
36 | _, err = asn1.Unmarshal(mdata2, z)
37 | fmt.Printf("Before marshal: %v, after unmarshal: %v\n", y, z)
38 | checkError(err)
39 |
40 | // structural error (incorrect Go type byte != rune)
41 | t3 := new(TheirType)
42 | _, err = asn1.Unmarshal(mdata1, t3)
43 | fmt.Printf("Before marshal: %v, after unmarshal: %v\n", t1, t3)
44 | checkError(err)
45 | }
46 |
47 | func checkError(err error) {
48 | if err != nil {
49 | log.Println(err.Error())
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/ch4/asn1pointers.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/asn1"
5 | "fmt"
6 | )
7 |
8 | type T struct {
9 | S string
10 | I int
11 | }
12 |
13 | func main() {
14 | // using variables
15 | t1 := T{"ok", 1}
16 | mdata1, _ := asn1.Marshal(t1)
17 | var newT1 T
18 | asn1.Unmarshal(mdata1, &newT1)
19 | fmt.Printf("Before marshal: %v, after unmarshal: %v\n", t1, newT1)
20 |
21 | // using pointers
22 | var t2 = new(T)
23 | t2.S = "still ok"
24 | t2.I = 2
25 | mdata2, _ := asn1.Marshal(*t2)
26 | var newT2 = new(T)
27 | asn1.Unmarshal(mdata2, newT2)
28 | fmt.Printf("Before marshal: %v, after unmarshal: %v\n", t2, newT2)
29 | }
30 |
--------------------------------------------------------------------------------
/ch4/asndaytimeclient.go:
--------------------------------------------------------------------------------
1 | /* ASN.1 DaytimeClient
2 | */
3 | package main
4 |
5 | import (
6 | "bytes"
7 | "encoding/asn1"
8 | "fmt"
9 | "io"
10 | "log"
11 | "net"
12 | "os"
13 | "time"
14 | )
15 |
16 | func main() {
17 | if len(os.Args) != 2 {
18 | log.Fatalln("Usage: %s host:port", os.Args[0])
19 | }
20 | service := os.Args[1]
21 | conn, err := net.Dial("tcp", service)
22 | checkError(err)
23 | result, err := readFully(conn)
24 | checkError(err)
25 | var newtime time.Time
26 | _, err1 := asn1.Unmarshal(result, &newtime)
27 | checkError(err1)
28 | fmt.Println("After marshal/unmarshal: ", newtime.String())
29 | }
30 |
31 | func readFully(conn net.Conn) ([]byte, error) {
32 | defer conn.Close()
33 | result := bytes.NewBuffer(nil)
34 | var buf [512]byte
35 | for {
36 | n, err := conn.Read(buf[0:])
37 | result.Write(buf[0:n])
38 | if err != nil {
39 | if err == io.EOF {
40 | break
41 | }
42 | return nil, err
43 | }
44 | }
45 | return result.Bytes(), nil
46 | }
47 |
48 | func checkError(err error) {
49 | if err != nil {
50 | log.Fatalln("Fatal error: %s", err.Error())
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/ch4/asndaytimeserver.go:
--------------------------------------------------------------------------------
1 | /* ASN1 DaytimeServer
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/asn1"
7 | "log"
8 | "net"
9 | "time"
10 | )
11 |
12 | func main() {
13 | service := ":1200"
14 | tcpAddr, err := net.ResolveTCPAddr("tcp", service)
15 | checkError(err)
16 | listener, err := net.ListenTCP("tcp", tcpAddr)
17 | checkError(err)
18 | for {
19 | conn, err := listener.Accept()
20 | if err != nil {
21 | continue
22 | }
23 | daytime := time.Now()
24 | // ignore returned errors
25 | mdata, _ := asn1.Marshal(daytime)
26 | conn.Write(mdata)
27 | conn.Close() // we're finished
28 | }
29 | }
30 |
31 | func checkError(err error) {
32 | if err != nil {
33 | log.Fatalln("Fatal error: %s", err.Error())
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ch4/badtype/code.go:
--------------------------------------------------------------------------------
1 | /* ./p/code.go
2 | */
3 | package p
4 |
5 | type T struct {
6 | f int
7 | F int
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/ch4/badtype/go.mod:
--------------------------------------------------------------------------------
1 | module badtype
2 |
3 | go 1.18
4 |
--------------------------------------------------------------------------------
/ch4/base64.go:
--------------------------------------------------------------------------------
1 | /*
2 | Base64
3 | */
4 | package main
5 |
6 | import (
7 | "encoding/base64"
8 | "fmt"
9 | )
10 |
11 | func main() {
12 | eightBitData := []byte{1, 2, 3, 4, 5, 6, 7, 8}
13 | enc := base64.StdEncoding.EncodeToString(eightBitData)
14 | dec, _ := base64.StdEncoding.DecodeString(enc)
15 | fmt.Println("Original data ", eightBitData)
16 | fmt.Println("Encoded string ", enc)
17 | fmt.Println("Decoded data ", dec)
18 | }
19 |
--------------------------------------------------------------------------------
/ch4/driver.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | /* driver.go
4 | */
5 |
6 | import (
7 | "encoding/asn1"
8 | "fmt"
9 | "badtype"
10 | )
11 |
12 | func main() {
13 | // using variables
14 | t1 := p.T{F:1}
15 | mdata1, err := asn1.Marshal(t1)
16 | fmt.Println(err)
17 | var newT1 p.T
18 | _, err = asn1.Unmarshal(mdata1, &newT1)
19 | fmt.Println(err)
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/ch4/gobechoclient.go:
--------------------------------------------------------------------------------
1 | /* Gob EchoClient
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/gob"
7 | "log"
8 | "net"
9 | "os"
10 | )
11 |
12 | type Person struct {
13 | Name Name
14 | Email []Email
15 | }
16 |
17 | type Name struct {
18 | Family string
19 | Personal string
20 | }
21 |
22 | type Email struct {
23 | Kind string
24 | Address string
25 | }
26 |
27 | func main() {
28 | if len(os.Args) != 2 {
29 | log.Fatalln("Usage: ", os.Args[0], "host:port")
30 | }
31 | person := Person{
32 | Name: Name{Family: "Newmarch", Personal: "Jan"},
33 | Email: []Email{
34 | Email{Kind: "home", Address: "jan@newmarch.name"},
35 | Email{Kind: "work", Address: "j.newmarch@boxhill.edu.au"},
36 | },
37 | }
38 | service := os.Args[1]
39 | conn, err := net.Dial("tcp", service)
40 | checkError(err)
41 | encoder := gob.NewEncoder(conn)
42 | decoder := gob.NewDecoder(conn)
43 | for n := 0; n < 10; n++ {
44 | encoder.Encode(person)
45 | var newPerson Person
46 | decoder.Decode(&newPerson)
47 |
48 | }
49 | }
50 |
51 | func checkError(err error) {
52 | if err != nil {
53 | log.Fatalln("Fatal error ", err.Error())
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ch4/gobechoserver.go:
--------------------------------------------------------------------------------
1 | /* Gob EchoServer
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/gob"
7 | "fmt"
8 | "log"
9 | "net"
10 | )
11 |
12 | type Person struct {
13 | Name Name
14 | Email []Email
15 | }
16 |
17 | type Name struct {
18 | Family string
19 | Personal string
20 | }
21 | type Email struct {
22 | Kind string
23 | Address string
24 | }
25 |
26 | func main() {
27 | service := "0.0.0.0:1200"
28 | tcpAddr, err := net.ResolveTCPAddr("tcp", service)
29 | checkError(err)
30 | listener, err := net.ListenTCP("tcp", tcpAddr)
31 | checkError(err)
32 | for {
33 | conn, err := listener.Accept()
34 | if err != nil {
35 | continue
36 | }
37 | encoder := gob.NewEncoder(conn)
38 | decoder := gob.NewDecoder(conn)
39 | for n := 0; n < 10; n++ {
40 | var person Person
41 | decoder.Decode(&person)
42 | fmt.Println(person)
43 | encoder.Encode(person)
44 | }
45 | conn.Close() // we're finished
46 | }
47 | }
48 |
49 | func checkError(err error) {
50 | if err != nil {
51 | log.Fatalln("Fatal error ", err.Error())
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ch4/jsonechoclient.go:
--------------------------------------------------------------------------------
1 | /* JSON EchoClient
2 | */
3 | package main
4 |
5 | import (
6 | "bytes"
7 | "encoding/json"
8 | "fmt"
9 | "io"
10 | "log"
11 | "net"
12 | "os"
13 | )
14 |
15 | type Person struct {
16 | Name Name
17 | Email []Email
18 | }
19 |
20 | type Name struct {
21 | Family string
22 | Personal string
23 | }
24 |
25 | type Email struct {
26 | Kind string
27 | Address string
28 | }
29 |
30 | func main() {
31 | if len(os.Args) != 2 {
32 | log.Fatalln("Usage: ", os.Args[0], "host:port")
33 | }
34 | person := Person{
35 | Name: Name{Family: "Newmarch", Personal: "Jan"},
36 | Email: []Email{
37 | Email{Kind: "home", Address: "jan@newmarch.name"},
38 | Email{Kind: "work", Address: "j.newmarch@boxhill.edu.au"},
39 | },
40 | }
41 | service := os.Args[1]
42 | conn, err := net.Dial("tcp", service)
43 | checkError(err)
44 | defer conn.Close()
45 | for n := 0; n < 10; n++ {
46 | data, _ := json.Marshal(person)
47 | conn.Write(data)
48 |
49 | var newPerson Person
50 | buf, _ := readFully(conn)
51 | err = json.Unmarshal(buf, &newPerson)
52 | fmt.Println(newPerson)
53 | }
54 | }
55 |
56 | func readFully(conn net.Conn) ([]byte, error) {
57 | result := bytes.NewBuffer(nil)
58 | var buf [512]byte
59 | for {
60 | n, err := conn.Read(buf[0:])
61 | result.Write(buf[0:n])
62 |
63 | if err != nil {
64 | if err == io.EOF {
65 | break
66 | }
67 | return nil, err
68 | }
69 | if n < 512 {
70 | break
71 | }
72 | }
73 | return result.Bytes(), nil
74 | }
75 |
76 | func checkError(err error) {
77 | if err != nil {
78 | log.Fatalln("Fatal error ", err.Error())
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/ch4/jsonechoserver.go:
--------------------------------------------------------------------------------
1 | /* JSON EchoServer
2 | */
3 | package main
4 |
5 | import (
6 | "bytes"
7 | "encoding/json"
8 | "fmt"
9 | "io"
10 | "net"
11 | "log"
12 | )
13 |
14 | type Person struct {
15 | Name Name
16 | Email []Email
17 | }
18 |
19 | type Name struct {
20 | Family string
21 | Personal string
22 | }
23 |
24 | type Email struct {
25 | Kind string
26 | Address string
27 | }
28 |
29 | func main() {
30 | service := "0.0.0.0:1200"
31 | tcpAddr, err := net.ResolveTCPAddr("tcp", service)
32 | checkError(err)
33 | listener, err := net.ListenTCP("tcp", tcpAddr)
34 | checkError(err)
35 | for {
36 | conn, err := listener.Accept()
37 | if err != nil {
38 | continue
39 | }
40 | for n := 0; n < 10; n++ {
41 | var person Person
42 | buf, _ := readFully(conn)
43 | err = json.Unmarshal(buf, &person)
44 |
45 | fmt.Println(person)
46 |
47 | data, _ := json.Marshal(person)
48 | conn.Write(data)
49 | }
50 | conn.Close() // we're finished
51 | }
52 | }
53 |
54 | func readFully(conn net.Conn) ([]byte, error) {
55 | result := bytes.NewBuffer(nil)
56 | var buf [512]byte
57 | for {
58 | n, err := conn.Read(buf[0:])
59 | result.Write(buf[0:n])
60 |
61 | if err != nil {
62 | if err == io.EOF {
63 | break
64 | }
65 | return nil, err
66 | }
67 | if n < 512 {
68 | break
69 | }
70 | }
71 | return result.Bytes(), nil
72 | }
73 |
74 | func checkError(err error) {
75 | if err != nil {
76 | log.Fatalln("Fatal error ", err.Error())
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/ch4/loadgob.go:
--------------------------------------------------------------------------------
1 | /* LoadGob
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/gob"
7 | "fmt"
8 | "log"
9 | "os"
10 | )
11 |
12 | type Person struct {
13 | Name Name
14 | Email []Email
15 | }
16 |
17 | type Name struct {
18 | Family string
19 | Personal string
20 | }
21 |
22 | type Email struct {
23 | Kind string
24 | Address string
25 | }
26 |
27 | func main() {
28 | var person Person
29 | loadGob("person.gob", &person)
30 | fmt.Println(person)
31 | }
32 |
33 | func loadGob(fileName string, key interface{}) {
34 | inFile, err := os.Open(fileName)
35 | checkError(err)
36 | decoder := gob.NewDecoder(inFile)
37 | err = decoder.Decode(key)
38 | checkError(err)
39 | inFile.Close()
40 | }
41 |
42 | func checkError(err error) {
43 | if err != nil {
44 | log.Fatalln("Fatal error ", err.Error())
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ch4/loadjson.go:
--------------------------------------------------------------------------------
1 | /* LoadJSON
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 | "os"
10 | )
11 |
12 | type Person struct {
13 | Name Name
14 | Email []Email
15 | }
16 |
17 | type Name struct {
18 | Family string
19 | Personal string
20 | }
21 |
22 | type Email struct {
23 | Kind string
24 | Address string
25 | }
26 |
27 | func main() {
28 | var person Person
29 | loadJSON("person.json", &person)
30 | fmt.Printf("%v\n", person)
31 | }
32 |
33 | func loadJSON(fileName string, key interface{}) {
34 | data, err := os.ReadFile(fileName)
35 | checkError(err)
36 | err = json.Unmarshal(data, key)
37 | checkError(err)
38 | }
39 |
40 | func checkError(err error) {
41 | if err != nil {
42 | log.Fatalln("Fatal error ", err.Error())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ch4/myapp/protocolbuffer.go:
--------------------------------------------------------------------------------
1 | /* ProtocolBuffer
2 | */
3 |
4 | package main
5 |
6 | import (
7 | "myapp/protos"
8 |
9 | "google.golang.org/protobuf/proto"
10 |
11 | "fmt"
12 | )
13 |
14 | func main() {
15 | name := protos.Person_Name{
16 | Family: "newmarch",
17 | Personal: "jan",
18 | }
19 | email1 := protos.Person_Email{
20 | Kind: "home",
21 | Address: "jan@newmarch.name",
22 | }
23 | email2 := protos.Person_Email{
24 | Kind: "work",
25 | Address: "j.newmarch@boxhill.edu.au",
26 | }
27 | emails := []*protos.Person_Email{&email1, &email2}
28 |
29 | p := protos.Person{
30 | Name: &name,
31 | Email: emails,
32 | }
33 |
34 | fmt.Println(p)
35 |
36 | data, _ := proto.Marshal(&p)
37 |
38 | newP := protos.Person{}
39 |
40 | proto.Unmarshal(data, &newP)
41 |
42 | fmt.Printf("%v\n", newP)
43 |
44 | if p.Name.Personal == newP.Name.Personal && p.Email[0].Address == newP.Email[0].Address {
45 | fmt.Println("same")
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/ch4/myapp/protos/personv3.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // versions:
3 | // protoc-gen-go v1.27.1
4 | // protoc v3.19.1
5 | // source: personv3.proto
6 |
7 | package protos
8 |
9 | import (
10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect"
11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl"
12 | reflect "reflect"
13 | sync "sync"
14 | )
15 |
16 | const (
17 | // Verify that this generated code is sufficiently up-to-date.
18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
19 | // Verify that runtime/protoimpl is sufficiently up-to-date.
20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
21 | )
22 |
23 | type Person struct {
24 | state protoimpl.MessageState
25 | sizeCache protoimpl.SizeCache
26 | unknownFields protoimpl.UnknownFields
27 |
28 | Name *Person_Name `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
29 | Email []*Person_Email `protobuf:"bytes,2,rep,name=email,proto3" json:"email,omitempty"`
30 | }
31 |
32 | func (x *Person) Reset() {
33 | *x = Person{}
34 | if protoimpl.UnsafeEnabled {
35 | mi := &file_personv3_proto_msgTypes[0]
36 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
37 | ms.StoreMessageInfo(mi)
38 | }
39 | }
40 |
41 | func (x *Person) String() string {
42 | return protoimpl.X.MessageStringOf(x)
43 | }
44 |
45 | func (*Person) ProtoMessage() {}
46 |
47 | func (x *Person) ProtoReflect() protoreflect.Message {
48 | mi := &file_personv3_proto_msgTypes[0]
49 | if protoimpl.UnsafeEnabled && x != nil {
50 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
51 | if ms.LoadMessageInfo() == nil {
52 | ms.StoreMessageInfo(mi)
53 | }
54 | return ms
55 | }
56 | return mi.MessageOf(x)
57 | }
58 |
59 | // Deprecated: Use Person.ProtoReflect.Descriptor instead.
60 | func (*Person) Descriptor() ([]byte, []int) {
61 | return file_personv3_proto_rawDescGZIP(), []int{0}
62 | }
63 |
64 | func (x *Person) GetName() *Person_Name {
65 | if x != nil {
66 | return x.Name
67 | }
68 | return nil
69 | }
70 |
71 | func (x *Person) GetEmail() []*Person_Email {
72 | if x != nil {
73 | return x.Email
74 | }
75 | return nil
76 | }
77 |
78 | type Person_Name struct {
79 | state protoimpl.MessageState
80 | sizeCache protoimpl.SizeCache
81 | unknownFields protoimpl.UnknownFields
82 |
83 | Family string `protobuf:"bytes,1,opt,name=family,proto3" json:"family,omitempty"`
84 | Personal string `protobuf:"bytes,2,opt,name=personal,proto3" json:"personal,omitempty"`
85 | }
86 |
87 | func (x *Person_Name) Reset() {
88 | *x = Person_Name{}
89 | if protoimpl.UnsafeEnabled {
90 | mi := &file_personv3_proto_msgTypes[1]
91 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
92 | ms.StoreMessageInfo(mi)
93 | }
94 | }
95 |
96 | func (x *Person_Name) String() string {
97 | return protoimpl.X.MessageStringOf(x)
98 | }
99 |
100 | func (*Person_Name) ProtoMessage() {}
101 |
102 | func (x *Person_Name) ProtoReflect() protoreflect.Message {
103 | mi := &file_personv3_proto_msgTypes[1]
104 | if protoimpl.UnsafeEnabled && x != nil {
105 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
106 | if ms.LoadMessageInfo() == nil {
107 | ms.StoreMessageInfo(mi)
108 | }
109 | return ms
110 | }
111 | return mi.MessageOf(x)
112 | }
113 |
114 | // Deprecated: Use Person_Name.ProtoReflect.Descriptor instead.
115 | func (*Person_Name) Descriptor() ([]byte, []int) {
116 | return file_personv3_proto_rawDescGZIP(), []int{0, 0}
117 | }
118 |
119 | func (x *Person_Name) GetFamily() string {
120 | if x != nil {
121 | return x.Family
122 | }
123 | return ""
124 | }
125 |
126 | func (x *Person_Name) GetPersonal() string {
127 | if x != nil {
128 | return x.Personal
129 | }
130 | return ""
131 | }
132 |
133 | type Person_Email struct {
134 | state protoimpl.MessageState
135 | sizeCache protoimpl.SizeCache
136 | unknownFields protoimpl.UnknownFields
137 |
138 | Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"`
139 | Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
140 | }
141 |
142 | func (x *Person_Email) Reset() {
143 | *x = Person_Email{}
144 | if protoimpl.UnsafeEnabled {
145 | mi := &file_personv3_proto_msgTypes[2]
146 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
147 | ms.StoreMessageInfo(mi)
148 | }
149 | }
150 |
151 | func (x *Person_Email) String() string {
152 | return protoimpl.X.MessageStringOf(x)
153 | }
154 |
155 | func (*Person_Email) ProtoMessage() {}
156 |
157 | func (x *Person_Email) ProtoReflect() protoreflect.Message {
158 | mi := &file_personv3_proto_msgTypes[2]
159 | if protoimpl.UnsafeEnabled && x != nil {
160 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
161 | if ms.LoadMessageInfo() == nil {
162 | ms.StoreMessageInfo(mi)
163 | }
164 | return ms
165 | }
166 | return mi.MessageOf(x)
167 | }
168 |
169 | // Deprecated: Use Person_Email.ProtoReflect.Descriptor instead.
170 | func (*Person_Email) Descriptor() ([]byte, []int) {
171 | return file_personv3_proto_rawDescGZIP(), []int{0, 1}
172 | }
173 |
174 | func (x *Person_Email) GetKind() string {
175 | if x != nil {
176 | return x.Kind
177 | }
178 | return ""
179 | }
180 |
181 | func (x *Person_Email) GetAddress() string {
182 | if x != nil {
183 | return x.Address
184 | }
185 | return ""
186 | }
187 |
188 | var File_personv3_proto protoreflect.FileDescriptor
189 |
190 | var file_personv3_proto_rawDesc = []byte{
191 | 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x76, 0x33, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
192 | 0x12, 0x06, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x22, 0xd0, 0x01, 0x0a, 0x06, 0x50, 0x65, 0x72,
193 | 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
194 | 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f,
195 | 0x6e, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x05,
196 | 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x65,
197 | 0x72, 0x73, 0x6f, 0x6e, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x61, 0x69,
198 | 0x6c, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x1a, 0x3a, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65,
199 | 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
200 | 0x52, 0x06, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x65, 0x72, 0x73,
201 | 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x65, 0x72, 0x73,
202 | 0x6f, 0x6e, 0x61, 0x6c, 0x1a, 0x35, 0x0a, 0x05, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x12, 0x0a,
203 | 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e,
204 | 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01,
205 | 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x09, 0x5a, 0x07, 0x2f,
206 | 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
207 | }
208 |
209 | var (
210 | file_personv3_proto_rawDescOnce sync.Once
211 | file_personv3_proto_rawDescData = file_personv3_proto_rawDesc
212 | )
213 |
214 | func file_personv3_proto_rawDescGZIP() []byte {
215 | file_personv3_proto_rawDescOnce.Do(func() {
216 | file_personv3_proto_rawDescData = protoimpl.X.CompressGZIP(file_personv3_proto_rawDescData)
217 | })
218 | return file_personv3_proto_rawDescData
219 | }
220 |
221 | var file_personv3_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
222 | var file_personv3_proto_goTypes = []interface{}{
223 | (*Person)(nil), // 0: person.Person
224 | (*Person_Name)(nil), // 1: person.Person.Name
225 | (*Person_Email)(nil), // 2: person.Person.Email
226 | }
227 | var file_personv3_proto_depIdxs = []int32{
228 | 1, // 0: person.Person.name:type_name -> person.Person.Name
229 | 2, // 1: person.Person.email:type_name -> person.Person.Email
230 | 2, // [2:2] is the sub-list for method output_type
231 | 2, // [2:2] is the sub-list for method input_type
232 | 2, // [2:2] is the sub-list for extension type_name
233 | 2, // [2:2] is the sub-list for extension extendee
234 | 0, // [0:2] is the sub-list for field type_name
235 | }
236 |
237 | func init() { file_personv3_proto_init() }
238 | func file_personv3_proto_init() {
239 | if File_personv3_proto != nil {
240 | return
241 | }
242 | if !protoimpl.UnsafeEnabled {
243 | file_personv3_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
244 | switch v := v.(*Person); i {
245 | case 0:
246 | return &v.state
247 | case 1:
248 | return &v.sizeCache
249 | case 2:
250 | return &v.unknownFields
251 | default:
252 | return nil
253 | }
254 | }
255 | file_personv3_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
256 | switch v := v.(*Person_Name); i {
257 | case 0:
258 | return &v.state
259 | case 1:
260 | return &v.sizeCache
261 | case 2:
262 | return &v.unknownFields
263 | default:
264 | return nil
265 | }
266 | }
267 | file_personv3_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
268 | switch v := v.(*Person_Email); i {
269 | case 0:
270 | return &v.state
271 | case 1:
272 | return &v.sizeCache
273 | case 2:
274 | return &v.unknownFields
275 | default:
276 | return nil
277 | }
278 | }
279 | }
280 | type x struct{}
281 | out := protoimpl.TypeBuilder{
282 | File: protoimpl.DescBuilder{
283 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
284 | RawDescriptor: file_personv3_proto_rawDesc,
285 | NumEnums: 0,
286 | NumMessages: 3,
287 | NumExtensions: 0,
288 | NumServices: 0,
289 | },
290 | GoTypes: file_personv3_proto_goTypes,
291 | DependencyIndexes: file_personv3_proto_depIdxs,
292 | MessageInfos: file_personv3_proto_msgTypes,
293 | }.Build()
294 | File_personv3_proto = out.File
295 | file_personv3_proto_rawDesc = nil
296 | file_personv3_proto_goTypes = nil
297 | file_personv3_proto_depIdxs = nil
298 | }
299 |
--------------------------------------------------------------------------------
/ch4/newbase64coders.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "fmt"
7 | "io"
8 | )
9 |
10 | var (
11 | enc *base64.Encoding = base64.StdEncoding.WithPadding('|')
12 | input []byte = []byte("∞a∞\x02ab")
13 | w bytes.Buffer
14 | )
15 |
16 | func restoreViaDecoder() {
17 | var buf *bytes.Buffer = bytes.NewBuffer(w.Bytes())
18 | var ior io.Reader = base64.NewDecoder(enc, buf)
19 | l := len(input)
20 |
21 | // adjust for unknown padding
22 | if l > 3 && l%3 != 0 {
23 | l = l + 2
24 | }
25 |
26 | restored := make([]byte, l)
27 | ior.Read(restored)
28 | fmt.Printf("%11s: %s %v\n", "viaDecoder", string(restored), restored)
29 | }
30 |
31 | func restoreViaEncoding() {
32 | var dst []byte = make([]byte, len(input))
33 | enc.Decode(dst, w.Bytes())
34 | fmt.Printf("%11s: %s %v\n", "viaEncoding", string(dst), dst)
35 | }
36 |
37 | func main() {
38 | fmt.Printf("%11s: %s %v\n", "input", string(input), input)
39 |
40 | var wc io.WriteCloser = base64.NewEncoder(enc, &w)
41 |
42 | wc.Write(input)
43 | wc.Close()
44 |
45 | fmt.Printf("%11s: %s %v\n", "ecoded", string(w.Bytes()), w.Bytes())
46 |
47 | restoreViaDecoder()
48 | restoreViaEncoding()
49 | }
50 |
--------------------------------------------------------------------------------
/ch4/personv3.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package = "/protos";
4 |
5 | package person;
6 |
7 | message Person {
8 | message Name {
9 | string family = 1;
10 | string personal = 2;
11 | }
12 | message Email {
13 | string kind = 1;
14 | string address = 2;
15 | }
16 | Name name = 1;
17 | repeated Email email = 2;
18 | }
19 |
--------------------------------------------------------------------------------
/ch4/prettyjson.go:
--------------------------------------------------------------------------------
1 | /* SaveJSON
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/json"
7 | "log"
8 | "os"
9 | )
10 |
11 | type Person struct {
12 | Name Name
13 | Email []Email
14 | }
15 | type Name struct {
16 | Family string
17 | Personal string
18 | }
19 | type Email struct {
20 | Kind string
21 | Address string
22 | }
23 |
24 | func main() {
25 | person := Person{
26 | Name: Name{Family: "Newmarch", Personal: "Jan"},
27 | Email: []Email{
28 | Email{Kind: "home", Address: "jan@newmarch.name"},
29 | Email{Kind: "work", Address: "j.newmarch@boxhill.edu.au"},
30 | },
31 | }
32 | prettyJSON("pretty_person.json", person)
33 | }
34 | func prettyJSON(fileName string, key interface{}) {
35 | data, err := json.MarshalIndent(key, " ", " ")
36 | checkError(err)
37 | err = os.WriteFile(fileName, data, 0600)
38 | checkError(err)
39 | }
40 | func checkError(err error) {
41 | if err != nil {
42 | log.Fatalln("Fatal error ", err.Error())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ch4/savegob.go:
--------------------------------------------------------------------------------
1 | /* SaveGob
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/gob"
7 | "os"
8 | "log"
9 | )
10 |
11 | type Person struct {
12 | Name Name
13 | Email []Email
14 | }
15 |
16 | type Name struct {
17 | Family string
18 | Personal string
19 | }
20 |
21 | type Email struct {
22 | Kind string
23 | Address string
24 | }
25 |
26 | func main() {
27 | person := Person{
28 | Name: Name{Family: "Newmarch", Personal: "Jan"},
29 | Email: []Email{
30 | Email{Kind: "home", Address: "jan@newmarch.name"},
31 | Email{Kind: "work", Address: "j.newmarch@boxhill.edu.au"},
32 | },
33 | }
34 | saveGob("person.gob", person)
35 | }
36 |
37 | func saveGob(fileName string, key interface{}) {
38 | outFile, err := os.Create(fileName)
39 | checkError(err)
40 | encoder := gob.NewEncoder(outFile)
41 | err = encoder.Encode(key)
42 | checkError(err)
43 | outFile.Close()
44 | }
45 |
46 | func checkError(err error) {
47 | if err != nil {
48 | log.Fatalln("Fatal error ", err.Error())
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ch4/savejson.go:
--------------------------------------------------------------------------------
1 | /* SaveJSON
2 | */
3 | package main
4 |
5 | import (
6 | "encoding/json"
7 | "log"
8 | "os"
9 | )
10 |
11 | type Person struct {
12 | Name Name
13 | Email []Email
14 | }
15 |
16 | type Name struct {
17 | Family string
18 | Personal string
19 | }
20 |
21 | type Email struct {
22 | Kind string
23 | Address string
24 | }
25 |
26 | func main() {
27 | person := Person{
28 | Name: Name{Family: "Newmarch", Personal: "Jan"},
29 | Email: []Email{
30 | Email{Kind: "home", Address: "jan@newmarch.name"},
31 | Email{Kind: "work", Address: "j.newmarch@boxhill.edu.au"},
32 | },
33 | }
34 | saveJSON("person.json", person)
35 | }
36 |
37 | func saveJSON(fileName string, key interface{}) {
38 | data, err := json.Marshal(key)
39 | checkError(err)
40 | err = os.WriteFile(fileName, data, 0600)
41 | checkError(err)
42 | }
43 |
44 | func checkError(err error) {
45 | if err != nil {
46 | log.Fatalln("Fatal error ", err.Error())
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ch5/ftpclient.go:
--------------------------------------------------------------------------------
1 | /* FTPClient
2 | */
3 | package main
4 |
5 | import (
6 | "bufio"
7 | "bytes"
8 | "fmt"
9 | "net"
10 | "os"
11 | "strings"
12 | )
13 |
14 | // strings used by the user interface
15 | const (
16 | uiDir = "dir"
17 | uiCd = "cd"
18 | uiPwd = "pwd"
19 | uiQuit = "quit"
20 | )
21 |
22 | // strings used across the network
23 | const (
24 | DIR = "DIR"
25 | CD = "CD"
26 | PWD = "PWD"
27 | )
28 |
29 | func main() {
30 | if len(os.Args) != 2 {
31 | fmt.Println("Usage: ", os.Args[0], "host")
32 | os.Exit(1)
33 | }
34 | host := os.Args[1]
35 | conn, err := net.Dial("tcp", host+":1202")
36 | checkError(err)
37 | reader := bufio.NewReader(os.Stdin)
38 | for {
39 | line, err := reader.ReadString('\n')
40 | // lose trailing whitespace
41 | line = strings.TrimRight(line, " \t\r\n")
42 | if err != nil {
43 | break
44 | }
45 | // split into command + arg
46 | strs := strings.SplitN(line, " ", 2)
47 | // decode user request
48 | switch strs[0] {
49 | case uiDir:
50 | dirRequest(conn)
51 | case uiCd:
52 | if len(strs) != 2 {
53 | fmt.Println("cd ")
54 | continue
55 | }
56 | fmt.Println("CD \"", strs[1], "\"")
57 | cdRequest(conn, strs[1])
58 | case uiPwd:
59 | pwdRequest(conn)
60 | case uiQuit:
61 | conn.Close()
62 | os.Exit(0)
63 | default:
64 | fmt.Println("Unknown command")
65 | }
66 | }
67 | }
68 |
69 | func dirRequest(conn net.Conn) {
70 | conn.Write([]byte(DIR + " "))
71 | var buf [512]byte
72 | result := bytes.NewBuffer(nil)
73 | for {
74 | // read till we hit a blank line
75 | n, _ := conn.Read(buf[0:])
76 | result.Write(buf[0:n])
77 | length := result.Len()
78 | contents := result.Bytes()
79 | if string(contents[length-4:]) == "\r\n\r\n" {
80 | fmt.Println(string(contents[0 : length-4]))
81 | return
82 | }
83 | }
84 | }
85 |
86 | func cdRequest(conn net.Conn, dir string) {
87 | conn.Write([]byte(CD + " " + dir))
88 | var response [512]byte
89 | n, _ := conn.Read(response[0:])
90 | s := string(response[0:n])
91 | if s != "OK" {
92 | fmt.Println("Failed to change dir")
93 | }
94 | }
95 |
96 | func pwdRequest(conn net.Conn) {
97 | conn.Write([]byte(PWD))
98 | var response [512]byte
99 | n, _ := conn.Read(response[0:])
100 | s := string(response[0:n])
101 | fmt.Println("Current dir \"" + s + "\"")
102 | }
103 |
104 | func checkError(err error) {
105 | if err != nil {
106 | fmt.Println("Fatal error ", err.Error())
107 | os.Exit(1)
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/ch5/ftpserver.go:
--------------------------------------------------------------------------------
1 | /* FTP Server
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "net"
8 | "os"
9 | )
10 |
11 | const (
12 | DIR = "DIR"
13 | CD = "CD"
14 | PWD = "PWD"
15 | )
16 |
17 | func main() {
18 | service := "0.0.0.0:1202"
19 | tcpAddr, err := net.ResolveTCPAddr("tcp", service)
20 | checkError(err)
21 | listener, err := net.ListenTCP("tcp", tcpAddr)
22 | checkError(err)
23 | for {
24 | conn, err := listener.Accept()
25 | if err != nil {
26 | continue
27 | }
28 | go handleClient(conn)
29 | }
30 | }
31 |
32 | func handleClient(conn net.Conn) {
33 | defer conn.Close()
34 | var buf [512]byte
35 | for {
36 | n, err := conn.Read(buf[0:])
37 | if err != nil {
38 | conn.Close()
39 | return
40 | }
41 | s := string(buf[0:n])
42 | // decode request
43 | if s[0:2] == CD {
44 | chdir(conn, s[3:])
45 | } else if s[0:3] == DIR {
46 | dirList(conn)
47 | } else if s[0:3] == PWD {
48 | pwd(conn)
49 | }
50 | }
51 | }
52 |
53 | func chdir(conn net.Conn, s string) {
54 | if os.Chdir(s) == nil {
55 | conn.Write([]byte("OK"))
56 | } else {
57 | conn.Write([]byte("ERROR"))
58 | }
59 | }
60 |
61 | func pwd(conn net.Conn) {
62 | s, err := os.Getwd()
63 | if err != nil {
64 | conn.Write([]byte(""))
65 | return
66 | }
67 | conn.Write([]byte(s))
68 | }
69 |
70 | func dirList(conn net.Conn) {
71 | // send a blank line on termination
72 | defer conn.Write([]byte("\r\n"))
73 | dir, err := os.Open(".")
74 | if err != nil {
75 | return
76 | }
77 | names, err := dir.Readdirnames(-1)
78 | if err != nil {
79 | return
80 | }
81 | for _, nm := range names {
82 | conn.Write([]byte(nm + "\r\n"))
83 | }
84 | }
85 |
86 | func checkError(err error) {
87 | if err != nil {
88 | fmt.Println("Fatal error ", err.Error())
89 | os.Exit(1)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/ch5/textprotoclient.go:
--------------------------------------------------------------------------------
1 | /* textproto
2 | */
3 |
4 | package main
5 |
6 | import (
7 | "fmt"
8 | "net/textproto"
9 | "os"
10 | )
11 |
12 | func checkerror(err error) {
13 | if err != nil {
14 | fmt.Println("error: ", err)
15 | os.Exit(1)
16 | }
17 | }
18 |
19 | func main() {
20 | conn, e := textproto.Dial("unix", "/tmp/fakewebserver")
21 | checkerror(e)
22 | defer conn.Close()
23 | fmt.Println("Sending request to retrieve /mypage")
24 | id, e := conn.Cmd("GET /mypage")
25 | checkerror(e)
26 | conn.StartResponse(id)
27 | defer conn.EndResponse(id)
28 | // fake sending back a 200 via nc or your own server
29 | code, stringResult, error := conn.ReadCodeLine(200)
30 | checkerror(e)
31 | fmt.Println(code, "\n", stringResult, "\n", error)
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/ch6/isotounicode.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "unicode/utf8"
6 | )
7 |
8 | func str2int(str string) []int {
9 | r := []rune(str)
10 | b := make([]int, utf8.RuneCountInString(str))
11 | for i, v := range r {
12 | b[i] = int(v)
13 | }
14 | return b
15 | }
16 |
17 | //unicode to 8859-4
18 | var unicodeToISOMap = map[int]uint8{
19 | // example match ascii 0x0021: 0x21, // !
20 | 0x012e: 0xc7, // Į
21 | 0x010c: 0xc8, // Č
22 | 0x0112: 0xaa, // Ē
23 | 0x0118: 0xca, // Ę
24 | // example match 0x00c9: 0xc9, // É
25 | // plus more
26 | }
27 |
28 | /* Turn a UTF-8 string into an ISO 8859 encoded byte array
29 | */
30 | func unicodeStrToISO(str string) []byte {
31 | // get the unicode code points
32 | codePoints := str2int(str) //[]int(str)
33 | // create a byte array of the same length
34 | bytes := make([]byte, len(codePoints))
35 | for n, v := range codePoints {
36 | // see if the point is in the exception map
37 | iso, ok := unicodeToISOMap[v]
38 | if !ok {
39 | // just use the value
40 | iso = uint8(v)
41 | }
42 | bytes[n] = iso
43 | }
44 | return bytes
45 | }
46 |
47 | // inverse of unicodeToISOMap
48 | var isoToUnicodeMap = map[uint8]int{
49 | 0xc7: 0x012e,
50 | 0xc8: 0x010c,
51 | 0xaa: 0x0112,
52 | 0xca: 0x0118,
53 | // and more
54 | }
55 |
56 | func isoBytesToUnicode(bytes []byte) string {
57 | codePoints := make([]int, len(bytes))
58 | for n, v := range bytes {
59 | unicode, ok := isoToUnicodeMap[v]
60 | if !ok {
61 | unicode = int(v)
62 | }
63 | codePoints[n] = unicode
64 | }
65 | return fmt.Sprintf("%q == %U", codePoints, codePoints)
66 | }
67 |
68 | func main() {
69 | x := "ĮĘa!"
70 | fmt.Printf("UTF-8: %s\n", x)
71 |
72 | fmt.Println("unicode to 8859-4")
73 | b := unicodeStrToISO(x)
74 | fmt.Printf("8859-4(hex): %x\n\n", b)
75 |
76 | fmt.Println("8859-4 to Unicode")
77 | fmt.Printf("Unicode: %v\n", isoBytesToUnicode(b))
78 | }
79 |
--------------------------------------------------------------------------------
/ch6/runeprint.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | str := "百度一下, 你就知道"
7 | fmt.Println("String length: ", len([]rune(str)))
8 | fmt.Println("Byte length: ", len(str))
9 | }
10 |
--------------------------------------------------------------------------------
/ch6/utf16client.go:
--------------------------------------------------------------------------------
1 | /* UTF16 Client
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net"
9 | "os"
10 | "unicode/utf16"
11 | )
12 |
13 | const BOM = '\ufffe'
14 |
15 | func main() {
16 | if len(os.Args) != 2 {
17 | log.Fatalln("Usage: ", os.Args[0], "host:port")
18 | }
19 | service := os.Args[1]
20 | conn, err := net.Dial("tcp", service)
21 | checkError(err)
22 | shorts := readShorts(conn)
23 | ints := utf16.Decode(shorts)
24 | str := string(ints)
25 | fmt.Println(str)
26 | }
27 | func readShorts(conn net.Conn) []uint16 {
28 | var buf [512]byte
29 | // read everything into the buffer
30 | n, err := conn.Read(buf[0:2]) // start with BOM
31 | for {
32 | m, err := conn.Read(buf[n:]) // read remaining byte pairs (originally unit16)
33 | if m == 0 || err != nil {
34 | break
35 | }
36 | n += m
37 | }
38 | checkError(err)
39 | var shorts []uint16
40 | shorts = make([]uint16, n/2)
41 |
42 | // We are checking for endianess
43 | // first - big endian 0xfffe
44 | // second - little endian 0xfeff
45 | // else - unknown
46 | // the inner loops are reading one byte per iteration
47 | // depending on endianess, places in the correct byte order
48 | // *warning* our server only supports big-endian
49 | if buf[0] == 0xff && buf[1] == 0xfe {
50 | for i := 2; i < n; i += 2 {
51 | shorts[i/2] = uint16(buf[i])<<8 + uint16(buf[i+1])
52 | }
53 | } else if buf[0] == 0xfe && buf[1] == 0xff {
54 | for i := 2; i < n; i += 2 {
55 | shorts[i/2] = uint16(buf[i+1])<<8 + uint16(buf[i])
56 | }
57 | } else {
58 | // unknown byte order
59 | fmt.Println("Unknown order")
60 | }
61 | return shorts
62 | }
63 | func checkError(err error) {
64 | if err != nil {
65 | log.Fatalln("Fatal error ", err.Error())
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/ch6/utf16encodedecode.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "unicode/utf16"
5 | "fmt"
6 | )
7 |
8 | func main() {
9 | str := "百度一下, 你就知道"
10 | fmt.Println("Before encoding:", str)
11 |
12 | runes := utf16.Encode([]rune(str))
13 | ints := utf16.Decode(runes)
14 |
15 | str = string(ints)
16 | fmt.Println("After encoding:", str)
17 | }
18 |
--------------------------------------------------------------------------------
/ch6/utf16server.go:
--------------------------------------------------------------------------------
1 | /* UTF16 Server
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "net"
8 | "unicode/utf16"
9 | )
10 |
11 | // warning, our server currently only supports big endian
12 | const BOM = '\ufeff'
13 |
14 | func main() {
15 | service := "0.0.0.0:1210"
16 | tcpAddr, err := net.ResolveTCPAddr("tcp", service)
17 | checkError(err)
18 | listener, err := net.ListenTCP("tcp", tcpAddr)
19 | checkError(err)
20 | for {
21 | conn, err := listener.Accept()
22 | if err != nil {
23 | continue
24 | }
25 | // eg. Ŵ is 0x0174, Ã is 0x00c3
26 | str := "Ŵj'ai arrêté"
27 | shorts := utf16.Encode([]rune(str))
28 | writeShorts(conn, shorts)
29 | conn.Close()
30 | }
31 | }
32 | func writeShorts(conn net.Conn, shorts []uint16) {
33 | var bytes [2]byte
34 | // send the BOM as first two bytes
35 | bytes[0] = BOM >> 8 // taking ff from BOM
36 | bytes[1] = BOM & 255 // taking fe from BOM
37 | _, err := conn.Write(bytes[0:]) // send BOM
38 | checkError(err)
39 | for _, v := range shorts {
40 | // breakup the unit16 into two bytes, then send
41 | bytes[0] = byte(v >> 8)
42 | bytes[1] = byte(v & 255)
43 | _, err = conn.Write(bytes[0:])
44 | if err != nil {
45 | return
46 | }
47 | }
48 | }
49 | func checkError(err error) {
50 | if err != nil {
51 | log.Fatalln("Fatal error ", err.Error())
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ch6/utfnorm.go:
--------------------------------------------------------------------------------
1 | /* UTFNorm
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "golang.org/x/text/unicode/norm"
8 | )
9 |
10 | func main() {
11 | str1 := "\u04d6"
12 | str2 := "\u0415\u0306"
13 | norm_str2 := norm.NFC.String(str2)
14 | bytes1 := []byte(str1)
15 | bytes2 := []byte(str2)
16 | norm_bytes2 := []byte(norm_str2)
17 | fmt.Println("Single char ", str1, " bytes ", bytes1)
18 | fmt.Println("Composed char ", str2, " bytes ", bytes2)
19 | fmt.Println("Normalized char", norm_str2, " bytes ",
20 | norm_bytes2)
21 | }
22 |
--------------------------------------------------------------------------------
/ch7/aes.go:
--------------------------------------------------------------------------------
1 | /* Aes
2 | */
3 | package main
4 |
5 | import (
6 | "bytes"
7 | "crypto/aes"
8 | "fmt"
9 | )
10 |
11 | func main() {
12 | key := []byte("my key, len 16 b")
13 | cipher, err := aes.NewCipher(key)
14 | if err != nil {
15 | fmt.Println(err.Error())
16 | }
17 | src := []byte("hello 16 b block")
18 | var enc [16]byte
19 | cipher.Encrypt(enc[0:], src)
20 | var decrypt [16]byte
21 | cipher.Decrypt(decrypt[0:], enc[0:])
22 | result := bytes.NewBuffer(nil)
23 | result.Write(decrypt[0:])
24 | fmt.Println(string(result.Bytes()))
25 | }
26 |
--------------------------------------------------------------------------------
/ch7/genrsakeys.go:
--------------------------------------------------------------------------------
1 | /* GenRSAKeys
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/rand"
7 | "crypto/rsa"
8 | "crypto/x509"
9 | "encoding/gob"
10 | "encoding/pem"
11 | "fmt"
12 | "log"
13 | "os"
14 | )
15 |
16 | func main() {
17 | reader := rand.Reader
18 | bitSize := 2048
19 | key, err := rsa.GenerateKey(reader, bitSize)
20 | checkError(err)
21 | fmt.Printf("Private key primes:\n[0]:%s\n[1]:%s\n", key.Primes[0].String(),
22 | key.Primes[1].String())
23 | fmt.Println("Private key exponent:\n", key.D.String())
24 | publicKey := key.PublicKey
25 | fmt.Println("Public key modulus:\n", publicKey.N.String())
26 | fmt.Println("Public key exponent:\n", publicKey.E)
27 | saveGobKey("private.key", key)
28 | saveGobKey("public.key", publicKey)
29 | savePEMKey("private.pem", key)
30 | }
31 | func saveGobKey(fileName string, key interface{}) {
32 | outFile, err := os.Create(fileName)
33 | checkError(err)
34 | encoder := gob.NewEncoder(outFile)
35 | err = encoder.Encode(key)
36 | checkError(err)
37 | outFile.Close()
38 | }
39 | func savePEMKey(fileName string, key *rsa.PrivateKey) {
40 | outFile, err := os.Create(fileName)
41 | checkError(err)
42 | var privateKey = &pem.Block{Type: "RSA PRIVATE KEY",
43 | Bytes: x509.MarshalPKCS1PrivateKey(key)}
44 | pem.Encode(outFile, privateKey)
45 | outFile.Close()
46 | }
47 | func checkError(err error) {
48 | if err != nil {
49 | log.Fatalln("Fatal error ", err.Error())
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/ch7/genx509cert.go:
--------------------------------------------------------------------------------
1 | /* GenX509Cert
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/rand"
7 | "crypto/rsa"
8 | "crypto/x509"
9 | "crypto/x509/pkix"
10 | "encoding/gob"
11 | "encoding/pem"
12 | "log"
13 | "math/big"
14 | "os"
15 | "time"
16 | )
17 |
18 | func main() {
19 | random := rand.Reader
20 | var key rsa.PrivateKey
21 | loadKey("private.key", &key)
22 | now := time.Now()
23 | then := now.Add(60 * 60 * 24 * 365 * 1000 * 1000 * 1000)
24 | // one year
25 | template := x509.Certificate{
26 | SerialNumber: big.NewInt(1),
27 | Subject: pkix.Name{
28 | CommonName: "jan.newmarch.name",
29 | Organization: []string{"Jan Newmarch"},
30 | },
31 | NotBefore: now,
32 | NotAfter: then,
33 | SubjectKeyId: []byte{1, 2, 3, 4},
34 | KeyUsage: x509.KeyUsageCertSign |
35 | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
36 | BasicConstraintsValid: true,
37 | IsCA: true,
38 | DNSNames: []string{"jan.newmarch.name",
39 | "localhost"},
40 | }
41 | derBytes, err := x509.CreateCertificate(random, &template,
42 | &template, &key.PublicKey, &key)
43 | checkError(err)
44 | certCerFile, err := os.Create("jan.newmarch.name.cer")
45 | checkError(err)
46 | certCerFile.Write(derBytes)
47 | certCerFile.Close()
48 | certPEMFile, err := os.Create("jan.newmarch.name.pem")
49 | checkError(err)
50 | pem.Encode(certPEMFile, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
51 | certPEMFile.Close()
52 | keyPEMFile, err := os.Create("private.pem")
53 | checkError(err)
54 | pem.Encode(keyPEMFile, &pem.Block{Type: "RSA PRIVATE KEY",
55 | Bytes: x509.MarshalPKCS1PrivateKey(&key)})
56 | keyPEMFile.Close()
57 | }
58 | func loadKey(fileName string, key interface{}) {
59 | inFile, err := os.Open(fileName)
60 | checkError(err)
61 | decoder := gob.NewDecoder(inFile)
62 | err = decoder.Decode(key)
63 | checkError(err)
64 | inFile.Close()
65 | }
66 | func checkError(err error) {
67 | if err != nil {
68 | log.Fatalln("Fatal error ", err.Error())
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/ch7/hmacmd5.go:
--------------------------------------------------------------------------------
1 | /* MD5Hash
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/md5"
7 | "crypto/hmac"
8 | "fmt"
9 | )
10 |
11 | func main() {
12 | hash := hmac.New(md5.New, []byte("secret"))
13 | bytes := []byte("hello\n")
14 | hash.Write(bytes)
15 | hashValue := hash.Sum(nil)
16 | hashSize := hash.Size()
17 | for n := 0; n < hashSize; n += 4 {
18 | var val uint32
19 | val = uint32(hashValue[n])<<24 +
20 | uint32(hashValue[n+1])<<16 +
21 | uint32(hashValue[n+2])<<8 +
22 | uint32(hashValue[n+3])
23 | fmt.Printf("%x ", val)
24 | }
25 | fmt.Println()
26 | }
27 |
--------------------------------------------------------------------------------
/ch7/largekey.go:
--------------------------------------------------------------------------------
1 | /* MD5Hash
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/sha256"
7 | "crypto/hmac"
8 | "fmt"
9 | )
10 |
11 | func main() {
12 | hash := hmac.New(sha256.New, []byte("2Secret!23"))
13 | bytes := []byte("hello\n")
14 | hash.Write(bytes)
15 | hashValue := hash.Sum(nil)
16 | hashSize := hash.Size()
17 | for n := 0; n < hashSize; n += 4 {
18 | var val uint32
19 | val = uint32(hashValue[n])<<24 +
20 | uint32(hashValue[n+1])<<16 +
21 | uint32(hashValue[n+2])<<8 +
22 | uint32(hashValue[n+3])
23 | fmt.Printf("%x ", val)
24 | }
25 | fmt.Println()
26 | }
27 |
--------------------------------------------------------------------------------
/ch7/loadrsakeys.go:
--------------------------------------------------------------------------------
1 | /* LoadRSAKeys
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/rsa"
7 | "encoding/gob"
8 | "fmt"
9 | "log"
10 | "os"
11 | )
12 |
13 | func main() {
14 | var key rsa.PrivateKey
15 | loadKey("private.key", &key)
16 | fmt.Printf("Private key primes:\n[0]:%s\n[1]:%s\n", key.Primes[0].String(),
17 | key.Primes[1].String())
18 | fmt.Println("Private key exponent:\n", key.D.String())
19 | var publicKey rsa.PublicKey
20 | loadKey("public.key", &publicKey)
21 | fmt.Println("Public key modulus:\n", publicKey.N.String())
22 | fmt.Println("Public key exponent:\n", publicKey.E)
23 | }
24 | func loadKey(fileName string, key interface{}) {
25 | inFile, err := os.Open(fileName)
26 | checkError(err)
27 | decoder := gob.NewDecoder(inFile)
28 | err = decoder.Decode(key)
29 | checkError(err)
30 | inFile.Close()
31 | }
32 | func checkError(err error) {
33 | if err != nil {
34 | log.Fatalln("Fatal error ", err.Error())
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ch7/loadx509cert.go:
--------------------------------------------------------------------------------
1 | /* LoadX509Cert
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/rsa"
7 | "crypto/x509"
8 | "encoding/gob"
9 | "fmt"
10 | "log"
11 | "os"
12 | )
13 |
14 | func main() {
15 | // load certificate so we can access embedded public key
16 | certCerFile, err := os.Open("jan.newmarch.name.cer")
17 | checkError(err)
18 | derBytes := make([]byte, 1000) // bigger than the file
19 | count, err := certCerFile.Read(derBytes)
20 | checkError(err)
21 | certCerFile.Close()
22 | // trim the bytes to actual length in call
23 | cert, err := x509.ParseCertificate(derBytes[0:count])
24 | checkError(err)
25 | fmt.Printf("Name %s\n", cert.Subject.CommonName)
26 | fmt.Printf("Not before %s\n", cert.NotBefore.String())
27 | fmt.Printf("Not after %s\n", cert.NotAfter.String())
28 |
29 | // load non-emdedded public key
30 | // should be the same as above embedded key
31 | pub, err := os.Open("public.key")
32 | checkError(err)
33 | dec := gob.NewDecoder(pub)
34 | publicKey := new(rsa.PrivateKey)
35 | err = dec.Decode(publicKey)
36 | checkError(err)
37 | pub.Close()
38 |
39 | // genx509cert.go created a public key and certificate
40 | // certificates also embed the public key
41 | // we are comparing the public key and the embedded public key fields
42 | // see go doc crypto/rsa.PublicKey for more
43 | if cert.PublicKey.(*rsa.PublicKey).N.Cmp(publicKey.N) == 0 {
44 | if publicKey.E == cert.PublicKey.(*rsa.PublicKey).E {
45 | fmt.Println("Same public key")
46 | return
47 | }
48 | }
49 | fmt.Println("Different public key")
50 | }
51 | func checkError(err error) {
52 | if err != nil {
53 | log.Fatalln("Fatal error ", err.Error())
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ch7/md5hash.go:
--------------------------------------------------------------------------------
1 | /* MD5Hash
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/md5"
7 | "fmt"
8 | )
9 |
10 | func main() {
11 | hash := md5.New()
12 | bytes := []byte("hello\n")
13 | hash.Write(bytes) // add data to running hash
14 | hashValue := hash.Sum(nil) // retrieve the hashed data
15 | hashSize := hash.Size() // how many bytes Sum returns (could just len(hashValue)
16 |
17 | // for every 4 bytes of hashValue
18 | // we stuff into an byte of val by shifting
19 | // val[first_byte] = hashValue[n] after shifting 24
20 | // val[second_byte = hashValue[n+1] after shifting 16
21 | // val[third_byte] = hashValue[n+2] after shifting 8
22 | // val[fourth_byte] = hashValue[n+3]
23 | // in the end, we have unint32 value that we print
24 | for n := 0; n < hashSize; n += 4 {
25 | var val uint32
26 | val = uint32(hashValue[n])<<24 +
27 | uint32(hashValue[n+1])<<16 +
28 | uint32(hashValue[n+2])<<8 +
29 | uint32(hashValue[n+3])
30 | fmt.Printf("%x ", val)
31 | }
32 | fmt.Println()
33 | }
34 |
--------------------------------------------------------------------------------
/ch7/sha256.go:
--------------------------------------------------------------------------------
1 | /* MD5Hash
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/hmac"
7 | "crypto/sha256"
8 | "fmt"
9 | )
10 |
11 | func main() {
12 | hash := hmac.New(sha256.New, []byte("secret"))
13 | bytes := []byte("hello\n")
14 | hash.Write(bytes)
15 | hashValue := hash.Sum(nil)
16 | hashSize := hash.Size()
17 | for n := 0; n < hashSize; n += 4 {
18 | var val uint32
19 | val = uint32(hashValue[n])<<24 +
20 | uint32(hashValue[n+1])<<16 +
21 | uint32(hashValue[n+2])<<8 +
22 | uint32(hashValue[n+3])
23 | fmt.Printf("%x ", val)
24 | }
25 | fmt.Println()
26 | }
27 |
--------------------------------------------------------------------------------
/ch7/tlsechoclient.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/tls"
5 | "crypto/x509"
6 | "fmt"
7 | "os"
8 | )
9 |
10 | func main() {
11 | rootPEM, err := os.ReadFile("jan.newmarch.name.pem")
12 | // First, create the set of root certificates. For this example we only
13 | // have one. It's also possible to omit this in order to use the
14 | // default root set of the current operating system.
15 | roots := x509.NewCertPool()
16 | if ok := roots.AppendCertsFromPEM(rootPEM); !ok {
17 | panic("failed to parse root certificate")
18 | }
19 |
20 | conn, err := tls.Dial("tcp", "localhost:1200", &tls.Config{
21 | RootCAs: roots,
22 | // InsecureSkipVerify: false,
23 | })
24 | if err != nil {
25 | panic("failed to connect: " + err.Error())
26 | }
27 |
28 | // Now write and read lots
29 | for n := 0; n < 10; n++ {
30 | fmt.Println("Writing...")
31 | conn.Write([]byte("Hello " + string(n+48)))
32 | var buf [512]byte
33 | n, _ := conn.Read(buf[0:])
34 | fmt.Println(string(buf[0:n]))
35 | }
36 |
37 | conn.Close()
38 | }
39 |
--------------------------------------------------------------------------------
/ch7/tlsechoserver.go:
--------------------------------------------------------------------------------
1 | /* TLSEchoServer
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/tls"
7 | "fmt"
8 | "log"
9 | "net"
10 | )
11 |
12 | func main() {
13 | cert, err := tls.LoadX509KeyPair("jan.newmarch.name.pem",
14 | "private.pem")
15 | checkError(err)
16 | config := tls.Config{Certificates: []tls.Certificate{cert}}
17 | service := "0.0.0.0:1200"
18 | listener, err := tls.Listen("tcp", service, &config)
19 | checkError(err)
20 | fmt.Println("Listening")
21 | for {
22 | conn, err := listener.Accept()
23 | if err != nil {
24 | fmt.Println(err.Error())
25 | continue
26 | }
27 | fmt.Println("Accepted")
28 | go handleClient(conn)
29 | }
30 | }
31 | func handleClient(conn net.Conn) {
32 | defer conn.Close()
33 | var buf [512]byte
34 | for {
35 | fmt.Println("Trying to read")
36 | n, err := conn.Read(buf[0:])
37 | if err != nil {
38 | fmt.Println(err)
39 | return
40 | }
41 | fmt.Println(string(buf[0:]))
42 | _, err = conn.Write(buf[0:n])
43 | if err != nil {
44 | return
45 | }
46 | }
47 | }
48 | func checkError(err error) {
49 | if err != nil {
50 | log.Fatalln("Fatal error ", err.Error())
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/ch7/tlsgethead.go:
--------------------------------------------------------------------------------
1 | /* TLSGetHead
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/tls"
7 | "fmt"
8 | "io/ioutil"
9 | "log"
10 | "os"
11 | )
12 |
13 | func main() {
14 | if len(os.Args) != 2 {
15 | log.Fatalln("Usage: ", os.Args[0], "host:port")
16 | }
17 | service := os.Args[1]
18 | // Dial over secure channel
19 | conn, err := tls.Dial("tcp", service, nil)
20 | checkError(err)
21 | _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
22 | checkError(err)
23 | result, err := ioutil.ReadAll(conn)
24 | checkError(err)
25 | fmt.Println(string(result))
26 | conn.Close()
27 | os.Exit(0)
28 | }
29 | func checkError(err error) {
30 | if err != nil {
31 | log.Fatalln("Fatal error ", err.Error())
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ch8/clientget.go:
--------------------------------------------------------------------------------
1 | /* ClientGet
2 | */
3 | package main
4 |
5 | import (
6 | "io"
7 | "fmt"
8 | "log"
9 | "net/http"
10 | "net/http/httputil"
11 | "net/url"
12 | "os"
13 | "strings"
14 | )
15 |
16 | func main() {
17 | if len(os.Args) != 2 {
18 | log.Fatalln("Usage: ", os.Args[0], "http://host:port/page")
19 | }
20 | url, err := url.Parse(os.Args[1])
21 | checkError(err)
22 | client := &http.Client{}
23 | request, err := http.NewRequest("HEAD", url.String(), nil)
24 | // only accept UTF-8
25 | request.Header.Add("Accept-Charset", "utf-8;q=1,ISO-8859-1;q=0")
26 | checkError(err)
27 | response, err := client.Do(request)
28 | checkError(err)
29 | if response.StatusCode != http.StatusOK {
30 | log.Fatalln(response.Status)
31 | }
32 | fmt.Println("The response header is")
33 | b, _ := httputil.DumpResponse(response, false)
34 | fmt.Print(string(b))
35 | chSet := getCharset(response)
36 | if chSet != "utf-8" {
37 | log.Fatalln("Cannot handle", chSet)
38 | }
39 | var buf [512]byte
40 | reader := response.Body
41 | fmt.Println("got body")
42 | for {
43 | n, err := reader.Read(buf[0:])
44 | if err != nil {
45 | if err == io.EOF {
46 | fmt.Print(string(buf[0:n]))
47 | break
48 | }
49 | checkError(err)
50 | }
51 | fmt.Print(string(buf[0:n]))
52 | }
53 | }
54 | func getCharset(response *http.Response) string {
55 | contentType := response.Header.Get("Content-Type")
56 | if contentType == "" {
57 | // guess
58 | return "utf-8"
59 | }
60 | idx := strings.Index(contentType, "charset=")
61 | if idx == -1 {
62 | // guess
63 | return "utf-8"
64 | }
65 | // we found charset now remove it
66 | chSet := strings.Trim(contentType[idx+8:], " ")
67 | return strings.ToLower(chSet)
68 | }
69 | func checkError(err error) {
70 | if err != nil {
71 | log.Fatalln("Fatal error ", err.Error())
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/ch8/fileserver.go:
--------------------------------------------------------------------------------
1 | /* File Server
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "net/http"
8 | )
9 |
10 | func main() {
11 | // deliver files from the directory /tmp/www
12 | fileServer := http.FileServer(http.Dir("/tmp/www"))
13 |
14 | // register the handler and deliver requests to it
15 | err := http.ListenAndServe(":8000", fileServer)
16 | if err != nil {
17 | log.Fatalln(err)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ch8/get.go:
--------------------------------------------------------------------------------
1 | /* Get
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "io"
8 | "log"
9 | "net/http"
10 | "net/http/httputil"
11 | "os"
12 | "strings"
13 | )
14 |
15 | func main() {
16 | if len(os.Args) != 2 {
17 | log.Fatalln("Usage: ", os.Args[0], "host:port")
18 | }
19 | url := os.Args[1]
20 | response, err := http.Get(url)
21 | checkError(err)
22 | if response.StatusCode != http.StatusOK {
23 | log.Fatalln(response.StatusCode)
24 | }
25 | fmt.Println("The response header is")
26 | b, _ := httputil.DumpResponse(response, false)
27 | fmt.Print(string(b))
28 | contentTypes := response.Header["Content-Type"]
29 | if !acceptableCharset(contentTypes) {
30 | log.Fatalln("Cannot handle", contentTypes)
31 | }
32 | fmt.Println("The response body is")
33 | var buf [512]byte
34 | reader := response.Body
35 | for {
36 | n, err := reader.Read(buf[0:])
37 | if err != nil {
38 | if err == io.EOF {
39 | fmt.Print(string(buf[0:n]))
40 | reader.Close()
41 | break
42 | }
43 | checkError(err)
44 | }
45 | fmt.Print(string(buf[0:n]))
46 | }
47 | }
48 | func acceptableCharset(contentTypes []string) bool {
49 | // each type is like [text/html; charset=utf-8]
50 | // we want the UTF-8 only
51 | for _, cType := range contentTypes {
52 | if strings.Index(cType, "utf-8") != -1 {
53 | return true
54 | }
55 | }
56 | return false
57 | }
58 |
59 | func checkError(err error) {
60 | if err != nil {
61 | log.Fatalln(err)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/ch8/head.go:
--------------------------------------------------------------------------------
1 | /* Head
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 | )
11 |
12 | func main() {
13 | if len(os.Args) != 2 {
14 | log.Fatalln("Usage: ", os.Args[0], "host:port")
15 | }
16 | url := os.Args[1]
17 | response, err := http.Head(url)
18 | if err != nil {
19 | log.Fatalln(err)
20 | }
21 | fmt.Println(response.Status)
22 | for k, v := range response.Header {
23 | fmt.Println(k+":", v)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ch8/httpsfileserver.go:
--------------------------------------------------------------------------------
1 | /* HTTPSFileServer
2 | */
3 | package main
4 |
5 | import (
6 | "net/http"
7 | "log"
8 | )
9 |
10 | func main() {
11 | // deliver files from the directory /tmp/www
12 | fileServer := http.FileServer(http.Dir("/tmp/www"))
13 | // register the handler and deliver requests to it
14 | err := http.ListenAndServeTLS(":8000", "jan.newmarch.name.pem",
15 | "private.pem", fileServer)
16 | if err != nil {
17 | log.Fatalln(err)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ch8/printenv.go:
--------------------------------------------------------------------------------
1 | /* Print Env
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "net/http"
8 | "os"
9 | )
10 |
11 | func main() {
12 | // file handler for most files
13 | fileServer := http.FileServer(http.Dir("/tmp/www"))
14 | http.Handle("/", fileServer)
15 | // function handler for /cgi-bin/printenv
16 | http.HandleFunc("/cgi-bin/printenv", printEnv)
17 |
18 | // deliver requests to the handlers
19 | err := http.ListenAndServe(":8000", nil)
20 | if err != nil {
21 | log.Fatalln(err)
22 | }
23 | }
24 | func printEnv(writer http.ResponseWriter, req *http.Request) {
25 | env := os.Environ()
26 | writer.Write([]byte("Environment
"))
27 | for _, v := range env {
28 | writer.Write([]byte(v + "\n"))
29 | }
30 | writer.Write([]byte("
"))
31 | }
32 |
--------------------------------------------------------------------------------
/ch8/proxyauthget.go:
--------------------------------------------------------------------------------
1 | /* ProxyAuthGet
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "encoding/base64"
8 | "fmt"
9 | "io"
10 | "net/http"
11 | "net/http/httputil"
12 | "net/url"
13 | "os"
14 | )
15 |
16 | const auth = "jannewmarch:mypassword"
17 |
18 | func main() {
19 | if len(os.Args) != 3 {
20 | log.Fatalln("Usage: ", os.Args[0], "http://proxy-host:port http://host:port/page")
21 | }
22 | proxy := os.Args[1]
23 | proxyURL, err := url.Parse(proxy)
24 | checkError(err)
25 | rawURL := os.Args[2]
26 | url, err := url.Parse(rawURL)
27 | checkError(err)
28 |
29 | // encode the auth
30 | basic := "Basic " +
31 | base64.StdEncoding.EncodeToString([]byte(auth))
32 |
33 | transport := &http.Transport{Proxy: http.ProxyURL(proxyURL)}
34 | client := &http.Client{Transport: transport}
35 |
36 | request, err := http.NewRequest("GET", url.String(), nil)
37 |
38 | request.Header.Add("Proxy-Authorization", basic)
39 | dump, _ := httputil.DumpRequest(request, false)
40 | fmt.Println(string(dump))
41 |
42 | // send the request
43 | response, err := client.Do(request)
44 |
45 | checkError(err)
46 | fmt.Println("Read ok")
47 | if response.StatusCode != http.StatusOK {
48 | log.Fatalln(response.Status)
49 | }
50 | fmt.Println("Response ok")
51 |
52 | var buf [512]byte
53 | reader := response.Body
54 | for {
55 | n, err := reader.Read(buf[0:])
56 | if err != nil {
57 | if err == io.EOF {
58 | fmt.Print(string(buf[0:n]))
59 | return
60 | }
61 | checkError(err)
62 | }
63 | fmt.Print(string(buf[0:n]))
64 | }
65 | os.Exit(0)
66 | }
67 | func checkError(err error) {
68 | if err != nil {
69 | log.Fatalln("Fatal error ", err.Error())
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/ch8/proxyget.go:
--------------------------------------------------------------------------------
1 | /* ProxyGet
2 | */
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "io"
8 | "log"
9 | "net/http"
10 | "net/url"
11 | "os"
12 | )
13 |
14 | func main() {
15 | if len(os.Args) != 2 {
16 | log.Fatalln("Usage: ", os.Args[0], "http://host:port/page")
17 | }
18 | rawURL := os.Args[1]
19 | url, err := url.Parse(rawURL)
20 | checkError(err)
21 |
22 | response, err := http.Get(url.String())
23 |
24 | checkError(err)
25 | fmt.Println("Read ok")
26 |
27 | if response.StatusCode != http.StatusOK {
28 | log.Fatalln(response.StatusCode)
29 | }
30 | fmt.Println("Response ok")
31 |
32 | var buf [512]byte
33 | reader := response.Body
34 | for {
35 | n, err := reader.Read(buf[0:])
36 | if err != nil {
37 | if err == io.EOF {
38 | fmt.Print(string(buf[0:n]))
39 | reader.Close()
40 | break
41 | }
42 | checkError(err)
43 | }
44 | fmt.Print(string(buf[0:n]))
45 | }
46 | }
47 | func checkError(err error) {
48 | if err != nil {
49 | log.Fatalln("Fatal error ", err.Error())
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/ch8/punycode.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "golang.org/x/net/idna"
6 | "net/url"
7 | )
8 |
9 | func main() {
10 | s := "https://日本語.jp:8443"
11 | r1, _ := idna.ToASCII(s)
12 | r2, _ := idna.ToUnicode(r1)
13 |
14 | fmt.Println(r1)
15 | fmt.Println(r2)
16 | fmt.Println(url.QueryEscape(s))
17 | }
18 |
--------------------------------------------------------------------------------
/ch8/serverhandler.go:
--------------------------------------------------------------------------------
1 | /* ServerHandler
2 | */
3 |
4 | package main
5 |
6 | import (
7 | "net/http"
8 | )
9 |
10 | func main() {
11 | myHandler := http.HandlerFunc(func(rw http.ResponseWriter,
12 | request *http.Request) {
13 |
14 | // Just return no content - arbitrary headers can be set, arbitrary body
15 | rw.WriteHeader(http.StatusNoContent)
16 | })
17 |
18 | http.ListenAndServe(":8080", myHandler)
19 | }
20 |
--------------------------------------------------------------------------------
/ch8/tlsunsafeclientget.go:
--------------------------------------------------------------------------------
1 | /* TLSUnsafeClientGet
2 | */
3 | package main
4 |
5 | import (
6 | "crypto/tls"
7 | "fmt"
8 | "log"
9 | "net/http"
10 | "net/url"
11 | "os"
12 | "strings"
13 | )
14 |
15 | func main() {
16 | if len(os.Args) != 2 {
17 | log.Fatalln("Usage: ", os.Args[0], "https://host:port/page")
18 | }
19 | url, err := url.Parse(os.Args[1])
20 | checkError(err)
21 | if url.Scheme != "https" {
22 | log.Fatalln("Not https scheme ", url.Scheme)
23 | }
24 |
25 | transport := &http.Transport{}
26 | transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: false}
27 | client := &http.Client{Transport: transport}
28 |
29 | request, err := http.NewRequest("GET", url.String(), nil)
30 | // only accept UTF-8
31 | checkError(err)
32 |
33 | response, err := client.Do(request)
34 | checkError(err)
35 |
36 | if response.StatusCode != http.StatusOK {
37 | log.Fatalln(response.Status)
38 | }
39 | fmt.Println("get a response")
40 |
41 | chSet := getCharset(response)
42 | fmt.Printf("got charset %s\n", chSet)
43 | if chSet != "UTF-8" {
44 | log.Fatalln("Cannot handle", chSet)
45 | }
46 |
47 | var buf [512]byte
48 | reader := response.Body
49 | fmt.Println("got body")
50 | for {
51 | n, err := reader.Read(buf[0:])
52 | checkError(err)
53 | fmt.Print(string(buf[0:n]))
54 | }
55 | }
56 | func getCharset(response *http.Response) string {
57 | contentType := response.Header.Get("Content-Type")
58 | if contentType == "" {
59 | // guess
60 | return "UTF-8"
61 | }
62 | idx := strings.Index(contentType, "charset:")
63 | if idx == -1 {
64 | // guess
65 | return "UTF-8"
66 | }
67 | return strings.Trim(contentType[idx:], " ")
68 | }
69 | func checkError(err error) {
70 | if err != nil {
71 | log.Fatalln("Fatal error ", err.Error())
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/ch9/printemails.go:
--------------------------------------------------------------------------------
1 | /* PrintEmails
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "os"
8 | "strings"
9 | "text/template"
10 | )
11 |
12 | type Person struct {
13 | Name string
14 | Emails []string
15 | }
16 |
17 | const templ = `The name is {{.Name}}.
18 | {{range .Emails}}
19 | An email is "{{. | emailExpand}}"
20 | {{end}}`
21 |
22 | func main() {
23 | person := Person{
24 | Name: "jan",
25 | Emails: []string{"jan@newmarch.name",
26 | "jan.newmarch@gmail.com"},
27 | }
28 | t, err := template.New("Person template").Funcs(
29 | template.FuncMap{
30 | "emailExpand": func(emailAddress string) string {
31 | return strings.Replace(emailAddress, "@", " at ", -1)
32 | },
33 | },
34 | ).Parse(templ)
35 |
36 | err = t.Execute(os.Stdout, person)
37 | checkError(err)
38 | }
39 |
40 | func checkError(err error) {
41 | if err != nil {
42 | log.Fatalln("Fatal error ", err.Error())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ch9/printjsonemails.go:
--------------------------------------------------------------------------------
1 | /**
2 | * PrintJSONEmails
3 | */
4 | package main
5 |
6 | import (
7 | "bytes"
8 | "encoding/json"
9 | "fmt"
10 | "log"
11 | "os"
12 | "text/template"
13 | )
14 |
15 | type Person struct {
16 | Name string
17 | Emails []string
18 | }
19 |
20 | const templ = `{"Name": "{{- .Name -}}", "Emails": [
21 | {{- range $index, $elmt := .Emails -}}
22 | {{- if $index -}}
23 | , "{{- $elmt -}}"
24 | {{- else -}}
25 | "{{- $elmt -}}"
26 | {{- end -}}
27 | {{- end -}}
28 | ] }`
29 |
30 | func main() {
31 | person := Person{
32 | Name: "jan",
33 | Emails: []string{"jan@newmarch.name",
34 | "jan.newmarch@gmail.com"},
35 | }
36 | t := template.New("Person template")
37 | t, err := t.Parse(templ)
38 | checkError(err)
39 | err = t.Execute(os.Stdout, person)
40 | checkError(err)
41 |
42 | // check via validity json package
43 | var b bytes.Buffer
44 | err = t.Execute(&b, person)
45 | checkError(err)
46 | if json.Valid(b.Bytes()) {
47 | fmt.Println("\nvalid json")
48 | }
49 | }
50 | func checkError(err error) {
51 | if err != nil {
52 | log.Fatalln("Fatal error ", err.Error())
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ch9/printnameemails.go:
--------------------------------------------------------------------------------
1 | /**
2 | * PrintNameEmails
3 | */
4 | package main
5 |
6 | import (
7 | "log"
8 | "os"
9 | "text/template"
10 | )
11 |
12 | type Person struct {
13 | Name string
14 | Emails []string
15 | }
16 |
17 | const templ = `{{$name := .Name}}
18 | {{ $numEmails := .Emails | len -}}
19 | {{range $idx, $email := .Emails -}}
20 | Name is {{$name}}, email {{$email}} is {{ $idx | increment }} of {{ $numEmails }}
21 | {{end}}
22 | `
23 |
24 | func main() {
25 | person := Person{
26 | Name: "jan",
27 | Emails: []string{"jan@newmarch.name",
28 | "jan.newmarch@gmail.com"},
29 | }
30 | t, err := template.New("Person template").Funcs(
31 | template.FuncMap{
32 | "increment": func(val int) int {
33 | return val + 1
34 | },
35 | },
36 | ).Parse(templ)
37 | checkError(err)
38 | err = t.Execute(os.Stdout, person)
39 | checkError(err)
40 | }
41 | func checkError(err error) {
42 | if err != nil {
43 | log.Fatalln("Fatal error ", err.Error())
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ch9/printperson.go:
--------------------------------------------------------------------------------
1 | /* PrintPerson
2 | */
3 | package main
4 |
5 | import (
6 | "log"
7 | "os"
8 | "text/template"
9 | )
10 |
11 | type Person struct {
12 | Name string
13 | Age int
14 | Emails []string
15 | Jobs []Job
16 | }
17 | type Job struct {
18 | Employer string
19 | Role string
20 | }
21 |
22 | const templ = `The name is {{.Name}}.
23 | The age is {{.Age}}.
24 | {{range .Emails}}
25 | An email is {{.}}
26 | {{end}}
27 | {{with .Jobs}}
28 | {{range .}}
29 | An employer is {{.Employer}}
30 | and the role is {{.Role}}
31 | {{end}}
32 | {{end}}
33 | `
34 |
35 | func main() {
36 | job1 := Job{Employer: "Box Hill Institute", Role: "Director, Commerce and ICT"}
37 | job2 := Job{Employer: "Canberra University", Role: "Adjunct Professor"}
38 | person := Person{
39 | Name: "jan",
40 | Age: 66,
41 | Emails: []string{"jan@newmarch.name",
42 | "jan.newmarch@gmail.com"},
43 | Jobs: []Job{job1, job2},
44 | }
45 | t := template.New("Person template")
46 | t, err := t.Parse(templ)
47 | checkError(err)
48 | err = t.Execute(os.Stdout, person)
49 | checkError(err)
50 | }
51 | func checkError(err error) {
52 | if err != nil {
53 | log.Fatalln("Fatal error ", err.Error())
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ch9/sequence.go:
--------------------------------------------------------------------------------
1 | /* Sequence.go
2 | * Copyright Roger Peppe
3 | */
4 | package main
5 |
6 | import (
7 | "errors"
8 | "fmt"
9 | "os"
10 | "text/template"
11 | )
12 |
13 | var tmpl = `{{$comma := sequence "" ", "}}
14 | {{range $}}{{$comma.Next}}{{.}}{{end}}
15 | {{$comma := sequence "" ", "}}
16 | {{$colour := cycle "black" "white" "red"}}
17 | {{range $}}{{$comma.Next}}{{.}} in {{$colour.Next}}{{end}}
18 | `
19 | var fmap = template.FuncMap{
20 | "sequence": sequenceFunc,
21 | "cycle": cycleFunc,
22 | }
23 |
24 | func main() {
25 | t, err := template.New("").Funcs(fmap).Parse(tmpl)
26 | if err != nil {
27 | fmt.Printf("parse error: %vn", err)
28 | return
29 | }
30 | err = t.Execute(os.Stdout, []string{"a", "b", "c",
31 | "d", "e", "f"})
32 | if err != nil {
33 | fmt.Printf("exec error: %vn", err)
34 | }
35 | }
36 |
37 | type generator struct {
38 | ss []string
39 | i int
40 | f func(s []string, i int) string
41 | }
42 |
43 | func (seq *generator) Next() string {
44 | s := seq.f(seq.ss, seq.i)
45 | seq.i++
46 | return s
47 | }
48 | func sequenceGen(ss []string, i int) string {
49 | if i >= len(ss) {
50 | return ss[len(ss)-1]
51 | }
52 | return ss[i]
53 | }
54 | func cycleGen(ss []string, i int) string {
55 | return ss[i%len(ss)]
56 | }
57 | func sequenceFunc(ss ...string) (*generator, error) {
58 | if len(ss) == 0 {
59 | return nil, errors.New("sequence must have at least one element")
60 | }
61 | return &generator{ss, 0, sequenceGen}, nil
62 | }
63 | func cycleFunc(ss ...string) (*generator, error) {
64 | if len(ss) == 0 {
65 | return nil, errors.New("cycle must have at least one element")
66 | }
67 | return &generator{ss, 0, cycleGen}, nil
68 | }
69 |
--------------------------------------------------------------------------------
/errata.md:
--------------------------------------------------------------------------------
1 | # Errata for *Network Programming with Go Language*
2 |
3 | On **page xx** [Summary of error]:
4 |
5 | Details of error here. Highlight key pieces in **bold**.
6 |
7 | ***
8 |
9 | On **page xx** [Summary of error]:
10 |
11 | Details of error here. Highlight key pieces in **bold**.
12 |
13 | ***
--------------------------------------------------------------------------------