Skip to content

Commit bde1691

Browse files
committed
Update http code
1 parent d7db230 commit bde1691

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.test
2+
*.cpu
3+
*.svg

http/test_http_v1/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
## Fix the race
2+
3+
If your code has data races, all bets are off and you're just waiting for a crash. The runtime promises nothing if you have a data race.
4+
5+
Multiple options:
6+
7+
- use channels
8+
- use a Mutex
9+
- use atomic
10+
11+
### Mutex
12+
13+
```go
14+
var visitors struct {
15+
sync.Mutex
16+
n int
17+
}
18+
...
19+
func foo() {
20+
...
21+
visitors.Lock()
22+
visitors.n++
23+
yourVisitorNumber := visitors.n
24+
visitors.Unlock()
25+
```
26+
[used here](./demo_test.go#L11-L14)
27+
28+
### Atomic
29+
30+
```go
31+
var visitors int64 // must be accessed atomically
32+
...
33+
func foo() {
34+
...
35+
visitNum := atomic.AddInt64(&visitors, 1)
36+
```

http/test_http_v1/demo.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/http"
7+
"regexp"
8+
"sync"
9+
)
10+
11+
var visitors struct {
12+
sync.Mutex
13+
n int
14+
}
15+
16+
func handleHi(w http.ResponseWriter, r *http.Request) {
17+
if match, _ := regexp.MatchString(`^\w*$`, r.FormValue("color")); !match {
18+
http.Error(w, "Optional color is invalid", http.StatusBadRequest)
19+
return
20+
}
21+
visitors.Lock()
22+
visitors.n++
23+
yourVisitorNumber := visitors.n
24+
visitors.Unlock()
25+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
26+
w.Write([]byte("<h1 style='color: " + r.FormValue("color") +
27+
"'>Welcome!</h1>You are visitor number " + fmt.Sprint(yourVisitorNumber) + "!"))
28+
}
29+
30+
func main() {
31+
log.Printf("Starting on port 8080")
32+
http.HandleFunc("/hi", handleHi)
33+
log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
34+
}

http/test_http_v1/demo_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"io/ioutil"
6+
"net/http"
7+
"net/http/httptest"
8+
"strings"
9+
"sync"
10+
"testing"
11+
)
12+
13+
func TestHandleRoot_Recorder(t *testing.T) {
14+
rw := httptest.NewRecorder()
15+
handleHi(rw, req(t, "GET / HTTP/1.0\r\n\r\n"))
16+
if !strings.Contains(rw.Body.String(), "visitor number") {
17+
t.Errorf("Unexpected output: %s", rw.Body)
18+
}
19+
}
20+
21+
func req(t testing.TB, v string) *http.Request {
22+
req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(v)))
23+
if err != nil {
24+
t.Fatal(err)
25+
}
26+
return req
27+
}
28+
29+
// 用真实的 client & server 测试
30+
func TestHandleHi_TestServer(t *testing.T) {
31+
ts := httptest.NewServer(http.HandlerFunc(handleHi))
32+
defer ts.Close()
33+
res, err := http.Get(ts.URL)
34+
t.Logf("URL: %s", ts.URL)
35+
if err != nil {
36+
t.Error(err)
37+
return
38+
}
39+
if g, w := res.Header.Get("Content-Type"), "text/html; charset=utf-8"; g != w {
40+
t.Errorf("Content-Type = %q; want %q", g, w)
41+
}
42+
slurp, err := ioutil.ReadAll(res.Body)
43+
defer res.Body.Close()
44+
45+
if err != nil {
46+
t.Error(err)
47+
return
48+
}
49+
t.Logf("Got: %s", slurp)
50+
}
51+
52+
// 测试并行
53+
func TestHandleHi_TestServer_Parallel(t *testing.T) {
54+
ts := httptest.NewServer(http.HandlerFunc(handleHi))
55+
defer ts.Close()
56+
var wg sync.WaitGroup
57+
for i := 0; i < 2; i++ {
58+
wg.Add(1)
59+
go func() {
60+
defer wg.Done()
61+
res, err := http.Get(ts.URL)
62+
if err != nil {
63+
t.Error(err)
64+
return
65+
}
66+
if g, w := res.Header.Get("Content-Type"), "text/html; charset=utf-8"; g != w {
67+
t.Errorf("Content-Type = %q; want %q", g, w)
68+
}
69+
slurp, err := ioutil.ReadAll(res.Body)
70+
defer res.Body.Close()
71+
if err != nil {
72+
t.Error(err)
73+
return
74+
}
75+
t.Logf("Got: %s", slurp)
76+
}()
77+
}
78+
wg.Wait()
79+
}
80+
81+
func BenchmarkHi(b *testing.B) {
82+
b.ReportAllocs()
83+
r := req(b, "GET / HTTP/1.0\r\n\r\n")
84+
for i := 0; i < b.N; i++ {
85+
rw := httptest.NewRecorder()
86+
handleHi(rw, r)
87+
}
88+
}

0 commit comments

Comments
 (0)