blob: e716c7e6c486cba8f8b696e9d522fb1c6f3769e3 [file] [log] [blame]
Andrew Gerrand71675c62010-06-30 16:56:30 +10001// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main
6
7import (
Andrew Gerrand71675c62010-06-30 16:56:30 +10008 "log"
Rob Pikef9489be2011-11-08 15:43:02 -08009 "net/http"
Andrew Gerrand71675c62010-06-30 16:56:30 +100010 "time"
11)
12
13const (
Stefan Nilsson1de49312012-01-19 14:45:59 +110014 numPollers = 2 // number of Poller goroutines to launch
15 pollInterval = 60 * time.Second // how often to poll each URL
16 statusInterval = 10 * time.Second // how often to log status to stdout
17 errTimeout = 10 * time.Second // back-off timeout on error
Andrew Gerrand71675c62010-06-30 16:56:30 +100018)
19
20var urls = []string{
21 "http://www.google.com/",
22 "http://golang.org/",
23 "http://blog.golang.org/",
24}
25
26// State represents the last-known state of a URL.
27type State struct {
28 url string
29 status string
30}
31
32// StateMonitor maintains a map that stores the state of the URLs being
33// polled, and prints the current state every updateInterval nanoseconds.
34// It returns a chan State to which resource state should be sent.
Stefan Nilsson1de49312012-01-19 14:45:59 +110035func StateMonitor(updateInterval time.Duration) chan<- State {
Andrew Gerrand71675c62010-06-30 16:56:30 +100036 updates := make(chan State)
37 urlStatus := make(map[string]string)
38 ticker := time.NewTicker(updateInterval)
39 go func() {
40 for {
41 select {
42 case <-ticker.C:
43 logState(urlStatus)
44 case s := <-updates:
45 urlStatus[s.url] = s.status
46 }
47 }
48 }()
49 return updates
50}
51
52// logState prints a state map.
53func logState(s map[string]string) {
Andrey Mirtchovskif1af2ec2010-11-08 09:58:57 -080054 log.Println("Current state:")
Andrew Gerrand71675c62010-06-30 16:56:30 +100055 for k, v := range s {
Andrey Mirtchovskif1af2ec2010-11-08 09:58:57 -080056 log.Printf(" %s %s", k, v)
Andrew Gerrand71675c62010-06-30 16:56:30 +100057 }
58}
59
60// Resource represents an HTTP URL to be polled by this program.
61type Resource struct {
62 url string
Stefan Nilsson1de49312012-01-19 14:45:59 +110063 errCount int
Andrew Gerrand71675c62010-06-30 16:56:30 +100064}
65
66// Poll executes an HTTP HEAD request for url
67// and returns the HTTP status string or an error string.
68func (r *Resource) Poll() string {
69 resp, err := http.Head(r.url)
70 if err != nil {
Andrey Mirtchovskif1af2ec2010-11-08 09:58:57 -080071 log.Println("Error", r.url, err)
Andrew Gerrand71675c62010-06-30 16:56:30 +100072 r.errCount++
Russ Cox44526cd2011-11-01 22:06:05 -040073 return err.Error()
Andrew Gerrand71675c62010-06-30 16:56:30 +100074 }
75 r.errCount = 0
76 return resp.Status
77}
78
79// Sleep sleeps for an appropriate interval (dependant on error state)
80// before sending the Resource to done.
Stefan Nilsson1de49312012-01-19 14:45:59 +110081func (r *Resource) Sleep(done chan<- *Resource) {
82 time.Sleep(pollInterval + errTimeout*time.Duration(r.errCount))
Andrew Gerrand71675c62010-06-30 16:56:30 +100083 done <- r
84}
85
86func Poller(in <-chan *Resource, out chan<- *Resource, status chan<- State) {
87 for r := range in {
88 s := r.Poll()
89 status <- State{r.url, s}
90 out <- r
91 }
92}
93
94func main() {
Stefan Nilsson1de49312012-01-19 14:45:59 +110095 // Create our input and output channels.
Andrew Gerrand71675c62010-06-30 16:56:30 +100096 pending, complete := make(chan *Resource), make(chan *Resource)
97
Stefan Nilsson1de49312012-01-19 14:45:59 +110098 // Launch the StateMonitor.
Andrew Gerrand71675c62010-06-30 16:56:30 +100099 status := StateMonitor(statusInterval)
100
Stefan Nilsson1de49312012-01-19 14:45:59 +1100101 // Launch some Poller goroutines.
Andrew Gerrand71675c62010-06-30 16:56:30 +1000102 for i := 0; i < numPollers; i++ {
103 go Poller(pending, complete, status)
104 }
105
Stefan Nilsson1de49312012-01-19 14:45:59 +1100106 // Send some Resources to the pending queue.
Andrew Gerrand71675c62010-06-30 16:56:30 +1000107 go func() {
108 for _, url := range urls {
109 pending <- &Resource{url: url}
110 }
111 }()
112
113 for r := range complete {
114 go r.Sleep(pending)
115 }
116}