1
2
3
4
5
6
7
8
9
10
11 package google
12
13 import (
14 "context"
15 "encoding/json"
16 "net/http"
17
18 "golang.org/x/blog/content/context/userip"
19 )
20
21
22 type Results []Result
23
24
25 type Result struct {
26 Title, URL string
27 }
28
29
30 func Search(ctx context.Context, query string) (Results, error) {
31
32 req, err := http.NewRequest("GET", "https://ajax.googleapis.com/ajax/services/search/web?v=1.0", nil)
33 if err != nil {
34 return nil, err
35 }
36 q := req.URL.Query()
37 q.Set("q", query)
38
39
40
41
42 if userIP, ok := userip.FromContext(ctx); ok {
43 q.Set("userip", userIP.String())
44 }
45 req.URL.RawQuery = q.Encode()
46
47
48
49 var results Results
50 err = httpDo(ctx, req, func(resp *http.Response, err error) error {
51 if err != nil {
52 return err
53 }
54 defer resp.Body.Close()
55
56
57
58 var data struct {
59 ResponseData struct {
60 Results []struct {
61 TitleNoFormatting string
62 URL string
63 }
64 }
65 }
66 if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
67 return err
68 }
69 for _, res := range data.ResponseData.Results {
70 results = append(results, Result{Title: res.TitleNoFormatting, URL: res.URL})
71 }
72 return nil
73 })
74
75
76 return results, err
77 }
78
79
80
81
82 func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {
83
84 c := make(chan error, 1)
85 req = req.WithContext(ctx)
86 go func() { c <- f(http.DefaultClient.Do(req)) }()
87 select {
88 case <-ctx.Done():
89 <-c
90 return ctx.Err()
91 case err := <-c:
92 return err
93 }
94 }
95
View as plain text