examples: Add longpoll server and client
This is an example of a race-free long-poll server and client. It uses a redirection method to signal that the "Watch" is running. Other race-free methods exist.
This commit is contained in:
52
examples/longpoll/redirect-client.go
Normal file
52
examples/longpoll/redirect-client.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// This is an example longpoll client. The connection to the corresponding
|
||||||
|
// server initiates a request on a "Watch". It then waits until a redirect is
|
||||||
|
// received from the server which indicates that the watch is ready. To signal
|
||||||
|
// than an event on this watch has occurred, the server sends a final message.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
timeout = 15
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Printf("Starting...")
|
||||||
|
|
||||||
|
checkRedirectFunc := func(req *http.Request, via []*http.Request) error {
|
||||||
|
log.Printf("Watch is ready!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: time.Duration(timeout) * time.Second,
|
||||||
|
CheckRedirect: checkRedirectFunc,
|
||||||
|
}
|
||||||
|
|
||||||
|
id := rand.Intn(2 ^ 32 - 1)
|
||||||
|
body := bytes.NewBufferString("hello")
|
||||||
|
url := fmt.Sprintf("http://127.0.0.1:12345/watch?id=%d", id)
|
||||||
|
req, err := http.NewRequest("GET", url, body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Event received: %+v", result)
|
||||||
|
|
||||||
|
s, err := ioutil.ReadAll(result.Body) // TODO: apparently we can stream
|
||||||
|
result.Body.Close()
|
||||||
|
log.Printf("Response: %+v", string(s))
|
||||||
|
}
|
||||||
56
examples/longpoll/redirect-server.go
Normal file
56
examples/longpoll/redirect-server.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// This is an example longpoll server. On client connection it starts a "Watch",
|
||||||
|
// and notifies the client with a redirect when that watch is ready. This is
|
||||||
|
// important to avoid a possible race between when the client believes the watch
|
||||||
|
// is actually ready, and when the server actually is watching.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// you can use `wget http://127.0.0.1:12345/hello -O /dev/null`
|
||||||
|
// or `go run client.go`
|
||||||
|
const (
|
||||||
|
addr = ":12345"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WatchStart kicks off the initial watch and then redirects the client to
|
||||||
|
// notify them that we're ready. The watch operation here is simulated.
|
||||||
|
func WatchStart(w http.ResponseWriter, req *http.Request) {
|
||||||
|
log.Printf("Start received...")
|
||||||
|
time.Sleep(time.Duration(5) * time.Second) // 5 seconds to get ready and start *our* watch ;)
|
||||||
|
//started := time.Now().UnixNano() // time since watch is "started"
|
||||||
|
log.Printf("URL: %+v", req.URL)
|
||||||
|
|
||||||
|
token := fmt.Sprintf("%d", rand.Intn(2^32-1))
|
||||||
|
http.Redirect(w, req, fmt.Sprintf("/ready?token=%s", token), http.StatusSeeOther) // TODO: which code should we use ?
|
||||||
|
log.Printf("Redirect sent!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchReady receives the client connection when it has been notified that the
|
||||||
|
// watch has started, and it returns to signal that an event on the watch
|
||||||
|
// occurred. The event operation here is simulated.
|
||||||
|
func WatchReady(w http.ResponseWriter, req *http.Request) {
|
||||||
|
log.Printf("Ready received")
|
||||||
|
log.Printf("URL: %+v", req.URL)
|
||||||
|
|
||||||
|
//time.Sleep(time.Duration(10) * time.Second)
|
||||||
|
time.Sleep(time.Duration(rand.Intn(10)) * time.Second) // wait until an "event" happens
|
||||||
|
|
||||||
|
io.WriteString(w, "Event happened!\n")
|
||||||
|
log.Printf("Event sent")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Printf("Starting...")
|
||||||
|
//rand.Seed(time.Now().UTC().UnixNano())
|
||||||
|
http.HandleFunc("/watch", WatchStart)
|
||||||
|
http.HandleFunc("/ready", WatchReady)
|
||||||
|
log.Printf("Listening on %s", addr)
|
||||||
|
log.Fatal(http.ListenAndServe(addr, nil))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user