//go:build OMIT // +build OMIT // The server program issues Google search requests and demonstrates the use of // the go.net Context API. It serves on port 8080. // // The /search endpoint accepts these query params: // q=the Google search query // timeout=a timeout for the request, in time.Duration format // // For example, http://localhost:8080/search?q=golang&timeout=1s serves the // first few Google search results for "golang" or a "deadline exceeded" error // if the timeout expires. package main import ( "context" "html/template" "log" "net/http" "time" "golang.org/x/blog/content/context/google" "golang.org/x/blog/content/context/userip" ) func main() { http.HandleFunc("/search", handleSearch) log.Fatal(http.ListenAndServe(":8080", nil)) } // handleSearch handles URLs like /search?q=golang&timeout=1s by forwarding the // query to google.Search. If the query param includes timeout, the search is // canceled after that duration elapses. func handleSearch(w http.ResponseWriter, req *http.Request) { // ctx is the Context for this handler. Calling cancel closes the // ctx.Done channel, which is the cancellation signal for requests // started by this handler. var ( ctx context.Context cancel context.CancelFunc ) timeout, err := time.ParseDuration(req.FormValue("timeout")) if err == nil { // The request has a timeout, so create a context that is // canceled automatically when the timeout expires. ctx, cancel = context.WithTimeout(context.Background(), timeout) } else { ctx, cancel = context.WithCancel(context.Background()) } defer cancel() // Cancel ctx as soon as handleSearch returns. // Check the search query. query := req.FormValue("q") if query == "" { http.Error(w, "no query", http.StatusBadRequest) return } // Store the user IP in ctx for use by code in other packages. userIP, err := userip.FromRequest(req) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } ctx = userip.NewContext(ctx, userIP) // Run the Google search and print the results. start := time.Now() results, err := google.Search(ctx, query) elapsed := time.Since(start) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if err := resultsTemplate.Execute(w, struct { Results google.Results Timeout, Elapsed time.Duration }{ Results: results, Timeout: timeout, Elapsed: elapsed, }); err != nil { log.Print(err) return } } var resultsTemplate = template.Must(template.New("results").Parse(`
    {{range .Results}}
  1. {{.Title}} - {{.URL}}
  2. {{end}}

{{len .Results}} results in {{.Elapsed}}; timeout {{.Timeout}}

`))