golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/blog/content/context/google/google.go (about) 1 // Package google provides a function to do Google searches using the Google Web 2 // Search API. See https://developers.google.com/web-search/docs/ 3 // 4 // This package is an example to accompany https://blog.golang.org/context. 5 // It is not intended for use by others. 6 // 7 // Google has since disabled its search API, 8 // and so this package is no longer useful. 9 package google 10 11 import ( 12 "encoding/json" 13 "net/http" 14 15 "golang.org/x/blog/content/context/userip" 16 "golang.org/x/net/context" 17 ) 18 19 // Results is an ordered list of search results. 20 type Results []Result 21 22 // A Result contains the title and URL of a search result. 23 type Result struct { 24 Title, URL string 25 } 26 27 // Search sends query to Google search and returns the results. 28 func Search(ctx context.Context, query string) (Results, error) { 29 // Prepare the Google Search API request. 30 req, err := http.NewRequest("GET", "https://ajax.googleapis.com/ajax/services/search/web?v=1.0", nil) 31 if err != nil { 32 return nil, err 33 } 34 q := req.URL.Query() 35 q.Set("q", query) 36 37 // If ctx is carrying the user IP address, forward it to the server. 38 // Google APIs use the user IP to distinguish server-initiated requests 39 // from end-user requests. 40 if userIP, ok := userip.FromContext(ctx); ok { 41 q.Set("userip", userIP.String()) 42 } 43 req.URL.RawQuery = q.Encode() 44 45 // Issue the HTTP request and handle the response. The httpDo function 46 // cancels the request if ctx.Done is closed. 47 var results Results 48 err = httpDo(ctx, req, func(resp *http.Response, err error) error { 49 if err != nil { 50 return err 51 } 52 defer resp.Body.Close() 53 54 // Parse the JSON search result. 55 // https://developers.google.com/web-search/docs/#fonje 56 var data struct { 57 ResponseData struct { 58 Results []struct { 59 TitleNoFormatting string 60 URL string 61 } 62 } 63 } 64 if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { 65 return err 66 } 67 for _, res := range data.ResponseData.Results { 68 results = append(results, Result{Title: res.TitleNoFormatting, URL: res.URL}) 69 } 70 return nil 71 }) 72 // httpDo waits for the closure we provided to return, so it's safe to 73 // read results here. 74 return results, err 75 } 76 77 // httpDo issues the HTTP request and calls f with the response. If ctx.Done is 78 // closed while the request or f is running, httpDo cancels the request, waits 79 // for f to exit, and returns ctx.Err. Otherwise, httpDo returns f's error. 80 func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error { 81 // Run the HTTP request in a goroutine and pass the response to f. 82 tr := &http.Transport{} 83 client := &http.Client{Transport: tr} 84 c := make(chan error, 1) 85 go func() { c <- f(client.Do(req)) }() 86 select { 87 case <-ctx.Done(): 88 tr.CancelRequest(req) 89 <-c // Wait for f to return. 90 return ctx.Err() 91 case err := <-c: 92 return err 93 } 94 }