Update SSE example
diff --git a/examples/sse/sse.go b/examples/sse/sse.go index 3043c43..50090a3 100644 --- a/examples/sse/sse.go +++ b/examples/sse/sse.go
@@ -19,14 +19,35 @@ "time" "github.com/fiorix/go-web/httpxtra" - "github.com/fiorix/go-web/sse" ) -func IndexHandler(w http.ResponseWriter, r *http.Request) { +func main() { + err := loadMovie("./ASCIImation.txt.gz") + if err != nil { + log.Println(err) + return + } + http.HandleFunc("/", mainHandler) + http.HandleFunc("/sse", sseHandler) + s := http.Server{ + Addr: ":8080", + Handler: httpxtra.Handler{Logger: logger}, + } + log.Fatal(s.ListenAndServe()) +} + +func mainHandler(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./index.html") } -func SSEHandler(w http.ResponseWriter, r *http.Request) { +func sseHandler(w http.ResponseWriter, r *http.Request) { + conn, ok := w.(http.Flusher) + if !ok { + http.Error(w, "Oops", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "text/event-stream") + w.WriteHeader(http.StatusOK) sf := 0 startFrame := r.FormValue("startFrame") if startFrame != "" { @@ -35,47 +56,21 @@ if sf < 0 || sf >= cap(frames) { sf = 0 } - conn, buf, err := sse.ServeEvents(w) - if err != nil { - log.Println(err) - return - } - defer conn.Close() // Play the movie, frame by frame - nbytes := 0 + lw := w.(*httpxtra.LogWriter) for n, f := range frames[sf:] { - nbytes += len(f.Buf) - m := &sse.MessageEvent{Id: strconv.Itoa(n + 1), Data: f.Buf} - if err = sse.SendEvent(buf, m); err != nil { - // Usually a broken pipe error. - // log.Println(e.Error()) - - // We update the bytes written to the handler so - // logging works fine. - if lw, ok := w.(*httpxtra.LogWriter); ok { - lw.Bytes += nbytes - } - break + _, err := fmt.Fprintf(w, "id: %d\ndata: %s\n\n", n+1, f.Buf) + if err != nil { + break // Client disconnected. + } + conn.Flush() + if lw != nil { + lw.Bytes += len(f.Buf) } time.Sleep(f.Time) } } -func main() { - err := loadMovie("./ASCIImation.txt.gz") - if err != nil { - log.Println(err) - return - } - http.HandleFunc("/", IndexHandler) - http.HandleFunc("/sse", SSEHandler) - s := http.Server{ - Addr: ":8080", - Handler: httpxtra.Handler{Logger: logger}, - } - log.Fatal(s.ListenAndServe()) -} - func logger(r *http.Request, created time.Time, status, bytes int) { fmt.Println(httpxtra.ApacheCommonLog(r, created, status, bytes)) }
diff --git a/sse/sse.go b/sse/sse.go deleted file mode 100644 index 13e39de..0000000 --- a/sse/sse.go +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright 2013-2014 The go-web authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Server-Sent events (SSE) -// http://dev.w3.org/html5/eventsource/ -// -// Usage example: -// -// func SSEHandler(w http.ResponseWriter, req *http.Request) { -// conn, err := sse.ServeEvents(w) -// if err != nil { -// http.Error(w, err.Error(), http.StatusInternalServerError) -// return -// } -// defer conn.Close() -// for i := 0; i < 10; i++ { -// sse.SendEvent(conn, &sse.MessageEvent{Data: "Hello, world"}) -// time.Sleep(1 * time.Second) -// } -// } -package sse - -import ( - "bufio" - "errors" - "fmt" - "net" - "net/http" -) - -var ErrNoHijack = errors.New("Server does not support hijacking") - -// MessageEvent is the container of Server-Sent events (SSE), push notifications. -type MessageEvent struct { - Data string // message content - Id string // id of the message (int?) - Event string // name of the event - Retry int // client reconnection time, in milliseconds -} - -// ServeEvents prepares the request for SSE, push notifications. -// Caveat: ResponseWriter.Status() returns 0 instead of 200 after ServeEvents -// is called, and might break logging. -func ServeEvents(w http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) { - w.Header().Set("Cache-Control", "no-cache") - w.Header().Set("Connection", "keep-alive") - w.Header().Set("Content-Type", "text/event-stream") - hj, ok := w.(http.Hijacker) - if !ok { - return nil, nil, ErrNoHijack - } - conn, rw, err := hj.Hijack() - if err != nil { - return nil, nil, err - } - fmt.Fprintf(rw, "HTTP/1.1 200 OK\r\n") - w.Header().Write(rw) - fmt.Fprintf(rw, "\r\n") - return conn, rw, rw.Flush() -} - -// SendEvent sends a push notification to the peer (usually a browser). -// Browsers can handle these events in JavaScript: -// http://www.w3schools.com/html/html5_serversentevents.asp -func SendEvent(rw *bufio.ReadWriter, m *MessageEvent) error { - if m.Data != "" { - fmt.Fprintf(rw, "data: %s\n", m.Data) - } - if m.Event != "" { - fmt.Fprintf(rw, "event: %s\n", m.Event) - } - if m.Id != "" { - fmt.Fprintf(rw, "id: %s\n", m.Id) - } - if m.Retry >= 1 { - fmt.Fprintf(rw, "retry: %d\n", m.Retry) - } - fmt.Fprintf(rw, "\n") - return rw.Flush() -}