github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/blog/content/context/server/server.go (about) 1 // The server program issues Google search requests and demonstrates the use of 2 // the go.net Context API. It serves on port 8080. 3 // 4 // The /search endpoint accepts these query params: 5 // q=the Google search query 6 // timeout=a timeout for the request, in time.Duration format 7 // 8 // For example, http://localhost:8080/search?q=golang&timeout=1s serves the 9 // first few Google search results for "golang" or a "deadline exceeded" error 10 // if the timeout expires. 11 package main 12 13 import ( 14 "html/template" 15 "log" 16 "net/http" 17 "time" 18 19 "golang.org/x/blog/content/context/google" 20 "golang.org/x/blog/content/context/userip" 21 "golang.org/x/net/context" 22 ) 23 24 func main() { 25 http.HandleFunc("/search", handleSearch) 26 log.Fatal(http.ListenAndServe(":8080", nil)) 27 } 28 29 // handleSearch handles URLs like /search?q=golang&timeout=1s by forwarding the 30 // query to google.Search. If the query param includes timeout, the search is 31 // canceled after that duration elapses. 32 func handleSearch(w http.ResponseWriter, req *http.Request) { 33 // ctx is the Context for this handler. Calling cancel closes the 34 // ctx.Done channel, which is the cancellation signal for requests 35 // started by this handler. 36 var ( 37 ctx context.Context 38 cancel context.CancelFunc 39 ) 40 timeout, err := time.ParseDuration(req.FormValue("timeout")) 41 if err == nil { 42 // The request has a timeout, so create a context that is 43 // canceled automatically when the timeout expires. 44 ctx, cancel = context.WithTimeout(context.Background(), timeout) 45 } else { 46 ctx, cancel = context.WithCancel(context.Background()) 47 } 48 defer cancel() // Cancel ctx as soon as handleSearch returns. 49 50 // Check the search query. 51 query := req.FormValue("q") 52 if query == "" { 53 http.Error(w, "no query", http.StatusBadRequest) 54 return 55 } 56 57 // Store the user IP in ctx for use by code in other packages. 58 userIP, err := userip.FromRequest(req) 59 if err != nil { 60 http.Error(w, err.Error(), http.StatusBadRequest) 61 return 62 } 63 ctx = userip.NewContext(ctx, userIP) 64 65 // Run the Google search and print the results. 66 start := time.Now() 67 results, err := google.Search(ctx, query) 68 elapsed := time.Since(start) 69 if err != nil { 70 http.Error(w, err.Error(), http.StatusInternalServerError) 71 return 72 } 73 if err := resultsTemplate.Execute(w, struct { 74 Results google.Results 75 Timeout, Elapsed time.Duration 76 }{ 77 Results: results, 78 Timeout: timeout, 79 Elapsed: elapsed, 80 }); err != nil { 81 log.Print(err) 82 return 83 } 84 } 85 86 var resultsTemplate = template.Must(template.New("results").Parse(` 87 <html> 88 <head/> 89 <body> 90 <ol> 91 {{range .Results}} 92 <li>{{.Title}} - <a href="{{.URL}}">{{.URL}}</a></li> 93 {{end}} 94 </ol> 95 <p>{{len .Results}} results in {{.Elapsed}}; timeout {{.Timeout}}</p> 96 </body> 97 </html> 98 `))