1
2
3
4
5
6
7
8
9
10
11
12
13
14 package main
15
16 import (
17 "context"
18 "html/template"
19 "log"
20 "net/http"
21 "time"
22
23 "golang.org/x/blog/content/context/google"
24 "golang.org/x/blog/content/context/userip"
25 )
26
27 func main() {
28 http.HandleFunc("/search", handleSearch)
29 log.Fatal(http.ListenAndServe(":8080", nil))
30 }
31
32
33
34
35 func handleSearch(w http.ResponseWriter, req *http.Request) {
36
37
38
39 var (
40 ctx context.Context
41 cancel context.CancelFunc
42 )
43 timeout, err := time.ParseDuration(req.FormValue("timeout"))
44 if err == nil {
45
46
47 ctx, cancel = context.WithTimeout(context.Background(), timeout)
48 } else {
49 ctx, cancel = context.WithCancel(context.Background())
50 }
51 defer cancel()
52
53
54 query := req.FormValue("q")
55 if query == "" {
56 http.Error(w, "no query", http.StatusBadRequest)
57 return
58 }
59
60
61 userIP, err := userip.FromRequest(req)
62 if err != nil {
63 http.Error(w, err.Error(), http.StatusBadRequest)
64 return
65 }
66 ctx = userip.NewContext(ctx, userIP)
67
68
69 start := time.Now()
70 results, err := google.Search(ctx, query)
71 elapsed := time.Since(start)
72 if err != nil {
73 http.Error(w, err.Error(), http.StatusInternalServerError)
74 return
75 }
76 if err := resultsTemplate.Execute(w, struct {
77 Results google.Results
78 Timeout, Elapsed time.Duration
79 }{
80 Results: results,
81 Timeout: timeout,
82 Elapsed: elapsed,
83 }); err != nil {
84 log.Print(err)
85 return
86 }
87 }
88
89 var resultsTemplate = template.Must(template.New("results").Parse(`
90 <html>
91 <head/>
92 <body>
93 <ol>
94 {{range .Results}}
95 <li>{{.Title}} - <a href="{{.URL}}">{{.URL}}</a></li>
96 {{end}}
97 </ol>
98 <p>{{len .Results}} results in {{.Elapsed}}; timeout {{.Timeout}}</p>
99 </body>
100 </html>
101 `))
102
View as plain text