| // Copyright 2010 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package main | 
 |  | 
 | import ( | 
 | 	"log" | 
 | 	"net/http" | 
 | 	"time" | 
 | ) | 
 |  | 
 | const ( | 
 | 	numPollers     = 2                // number of Poller goroutines to launch | 
 | 	pollInterval   = 60 * time.Second // how often to poll each URL | 
 | 	statusInterval = 10 * time.Second // how often to log status to stdout | 
 | 	errTimeout     = 10 * time.Second // back-off timeout on error | 
 | ) | 
 |  | 
 | var urls = []string{ | 
 | 	"http://www.google.com/", | 
 | 	"http://golang.org/", | 
 | 	"http://blog.golang.org/", | 
 | } | 
 |  | 
 | // State represents the last-known state of a URL. | 
 | type State struct { | 
 | 	url    string | 
 | 	status string | 
 | } | 
 |  | 
 | // StateMonitor maintains a map that stores the state of the URLs being | 
 | // polled, and prints the current state every updateInterval nanoseconds. | 
 | // It returns a chan State to which resource state should be sent. | 
 | func StateMonitor(updateInterval time.Duration) chan<- State { | 
 | 	updates := make(chan State) | 
 | 	urlStatus := make(map[string]string) | 
 | 	ticker := time.NewTicker(updateInterval) | 
 | 	go func() { | 
 | 		for { | 
 | 			select { | 
 | 			case <-ticker.C: | 
 | 				logState(urlStatus) | 
 | 			case s := <-updates: | 
 | 				urlStatus[s.url] = s.status | 
 | 			} | 
 | 		} | 
 | 	}() | 
 | 	return updates | 
 | } | 
 |  | 
 | // logState prints a state map. | 
 | func logState(s map[string]string) { | 
 | 	log.Println("Current state:") | 
 | 	for k, v := range s { | 
 | 		log.Printf(" %s %s", k, v) | 
 | 	} | 
 | } | 
 |  | 
 | // Resource represents an HTTP URL to be polled by this program. | 
 | type Resource struct { | 
 | 	url      string | 
 | 	errCount int | 
 | } | 
 |  | 
 | // Poll executes an HTTP HEAD request for url | 
 | // and returns the HTTP status string or an error string. | 
 | func (r *Resource) Poll() string { | 
 | 	resp, err := http.Head(r.url) | 
 | 	if err != nil { | 
 | 		log.Println("Error", r.url, err) | 
 | 		r.errCount++ | 
 | 		return err.Error() | 
 | 	} | 
 | 	r.errCount = 0 | 
 | 	return resp.Status | 
 | } | 
 |  | 
 | // Sleep sleeps for an appropriate interval (dependent on error state) | 
 | // before sending the Resource to done. | 
 | func (r *Resource) Sleep(done chan<- *Resource) { | 
 | 	time.Sleep(pollInterval + errTimeout*time.Duration(r.errCount)) | 
 | 	done <- r | 
 | } | 
 |  | 
 | func Poller(in <-chan *Resource, out chan<- *Resource, status chan<- State) { | 
 | 	for r := range in { | 
 | 		s := r.Poll() | 
 | 		status <- State{r.url, s} | 
 | 		out <- r | 
 | 	} | 
 | } | 
 |  | 
 | func main() { | 
 | 	// Create our input and output channels. | 
 | 	pending, complete := make(chan *Resource), make(chan *Resource) | 
 |  | 
 | 	// Launch the StateMonitor. | 
 | 	status := StateMonitor(statusInterval) | 
 |  | 
 | 	// Launch some Poller goroutines. | 
 | 	for i := 0; i < numPollers; i++ { | 
 | 		go Poller(pending, complete, status) | 
 | 	} | 
 |  | 
 | 	// Send some Resources to the pending queue. | 
 | 	go func() { | 
 | 		for _, url := range urls { | 
 | 			pending <- &Resource{url: url} | 
 | 		} | 
 | 	}() | 
 |  | 
 | 	for r := range complete { | 
 | 		go r.Sleep(pending) | 
 | 	} | 
 | } |