blob: 16d744ab4de7f4db7d984dd2d381228deb701f86 [file] [log] [blame]
// +build OMIT
// The server program issues Google search requests. It serves on port 8080.
//
// The /search endpoint accepts these query params:
// q=the Google search query
//
// For example, http://localhost:8080/search?q=golang serves the first
// few Google search results for "golang".
package main
import (
"encoding/json"
"fmt"
"html/template"
"log"
"net/http"
"net/url"
"time"
)
func main() {
http.HandleFunc("/search", handleSearch) // HL
fmt.Println("serving on http://localhost:8080/search")
log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
// handleSearch handles URLs like "/search?q=golang" by running a
// Google search for "golang" and writing the results as HTML to w.
func handleSearch(w http.ResponseWriter, req *http.Request) {
log.Println("serving", req.URL)
// Check the search query.
query := req.FormValue("q") // HL
if query == "" {
http.Error(w, `missing "q" URL parameter`, http.StatusBadRequest)
return
}
// ENDQUERY OMIT
// Run the Google search.
start := time.Now()
results, err := Search(query) // HL
elapsed := time.Since(start)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// ENDSEARCH OMIT
// Render the results.
type templateData struct {
Results []Result
Elapsed time.Duration
}
if err := resultsTemplate.Execute(w, templateData{ // HL
Results: results,
Elapsed: elapsed,
}); err != nil {
log.Print(err)
return
}
// ENDRENDER OMIT
}
// A Result contains the title and URL of a search result.
type Result struct { // HL
Title, URL string // HL
} // HL
var resultsTemplate = template.Must(template.New("results").Parse(`
<html>
<head/>
<body>
<ol>
{{range .Results}}
<li>{{.Title}} - <a href="{{.URL}}">{{.URL}}</a></li>
{{end}}
</ol>
<p>{{len .Results}} results in {{.Elapsed}}</p>
</body>
</html>
`))
// Search sends query to Google search and returns the results.
func Search(query string) ([]Result, error) {
// Prepare the Google Search API request.
u, err := url.Parse("https://ajax.googleapis.com/ajax/services/search/web?v=1.0")
if err != nil {
return nil, err
}
q := u.Query()
q.Set("q", query) // HL
u.RawQuery = q.Encode()
// Issue the HTTP request and handle the response.
resp, err := http.Get(u.String()) // HL
if err != nil {
return nil, err
}
defer resp.Body.Close() // HL
// Parse the JSON search result.
// https://developers.google.com/web-search/docs/#fonje
var jsonResponse struct {
ResponseData struct {
Results []struct {
TitleNoFormatting, URL string
}
}
}
if err := json.NewDecoder(resp.Body).Decode(&jsonResponse); err != nil { // HL
return nil, err
}
// Extract the Results from jsonResponse and return them.
var results []Result
for _, r := range jsonResponse.ResponseData.Results { // HL
results = append(results, Result{Title: r.TitleNoFormatting, URL: r.URL})
}
return results, nil
}