diff options
Diffstat (limited to 'content/context/server/server.go')
-rw-r--r-- | content/context/server/server.go | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/content/context/server/server.go b/content/context/server/server.go new file mode 100644 index 0000000..bbab5b0 --- /dev/null +++ b/content/context/server/server.go @@ -0,0 +1,98 @@ +// 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 ( + "html/template" + "log" + "net/http" + "time" + + "code.google.com/p/go.blog/content/context/google" + "code.google.com/p/go.blog/content/context/userip" + "code.google.com/p/go.net/context" +) + +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> +`)) |