blob: 13e39de67a598c067c17db4a10c80c16dbee0ed4 [file] [log] [blame]
// 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()
}