| // +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(` |
| <html> |
| <head/> |
| <body> |
| <ol> |
| {{range .Results}} |
| <li>{{.Title}} - <a href="{{.URL}}">{{.URL}}</a></li> |
| {{end}} |
| </ol> |
| <p>{{len .Results}} results in {{.Elapsed}}; timeout {{.Timeout}}</p> |
| </body> |
| </html> |
| `)) |