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  `))