// HandleWS implements a websocket handler. func (w *WatchServer) HandleWS(ws *websocket.Conn) { done := make(chan struct{}) go func() { var unused interface{} // Expect this to block until the connection is closed. Client should not // send anything. websocket.JSON.Receive(ws, &unused) close(done) }() for { select { case <-done: w.watching.Stop() return case event, ok := <-w.watching.ResultChan(): if !ok { // End of results. return } obj, err := api.NewJSONWatchEvent(w.codec, event) if err != nil { // Client disconnect. w.watching.Stop() return } if err := websocket.JSON.Send(ws, obj); err != nil { // Client disconnect. w.watching.Stop() return } } } }
// ServeHTTP serves a series of JSON encoded events via straight HTTP with // Transfer-Encoding: chunked. func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { loggedW := httplog.LogOf(req, w) w = httplog.Unlogged(w) cn, ok := w.(http.CloseNotifier) if !ok { loggedW.Addf("unable to get CloseNotifier") http.NotFound(w, req) return } flusher, ok := w.(http.Flusher) if !ok { loggedW.Addf("unable to get Flusher") http.NotFound(w, req) return } w.Header().Set("Transfer-Encoding", "chunked") w.WriteHeader(http.StatusOK) flusher.Flush() encoder := json.NewEncoder(w) for { select { case <-cn.CloseNotify(): self.watching.Stop() return case event, ok := <-self.watching.ResultChan(): if !ok { // End of results. return } obj, err := api.NewJSONWatchEvent(self.codec, event) if err != nil { // Client disconnect. self.watching.Stop() return } if err := encoder.Encode(obj); err != nil { // Client disconnect. self.watching.Stop() return } flusher.Flush() } } }
func TestWatch(t *testing.T) { var table = []struct { t watch.EventType obj runtime.Object }{ {watch.Added, &api.Pod{JSONBase: api.JSONBase{ID: "first"}}}, {watch.Modified, &api.Pod{JSONBase: api.JSONBase{ID: "second"}}}, {watch.Deleted, &api.Pod{JSONBase: api.JSONBase{ID: "third"}}}, } auth := AuthInfo{User: "******", Password: "******"} testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { checkAuth(t, auth, r) flusher, ok := w.(http.Flusher) if !ok { panic("need flusher!") } w.Header().Set("Transfer-Encoding", "chunked") w.WriteHeader(http.StatusOK) flusher.Flush() encoder := json.NewEncoder(w) for _, item := range table { data, err := api.NewJSONWatchEvent(latest.Codec, watch.Event{item.t, item.obj}) if err != nil { panic(err) } if err := encoder.Encode(data); err != nil { panic(err) } flusher.Flush() } })) s, err := New(testServer.URL, &auth) if err != nil { t.Fatalf("unexpected error: %v", err) } watching, err := s.Get().Path("path/to/watch/thing").Watch() if err != nil { t.Fatalf("Unexpected error") } for _, item := range table { got, ok := <-watching.ResultChan() if !ok { t.Fatalf("Unexpected early close") } if e, a := item.t, got.Type; e != a { t.Errorf("Expected %v, got %v", e, a) } if e, a := item.obj, got.Object; !reflect.DeepEqual(e, a) { t.Errorf("Expected %v, got %v", e, a) } } _, ok := <-watching.ResultChan() if ok { t.Fatal("Unexpected non-close") } }