gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

sse-encoder.go (2256B)


      1 // Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
      2 // Use of this source code is governed by a MIT style
      3 // license that can be found in the LICENSE file.
      4 
      5 package sse
      6 
      7 import (
      8 	"encoding/json"
      9 	"fmt"
     10 	"io"
     11 	"net/http"
     12 	"reflect"
     13 	"strconv"
     14 	"strings"
     15 )
     16 
     17 // Server-Sent Events
     18 // W3C Working Draft 29 October 2009
     19 // http://www.w3.org/TR/2009/WD-eventsource-20091029/
     20 
     21 const ContentType = "text/event-stream"
     22 
     23 var contentType = []string{ContentType}
     24 var noCache = []string{"no-cache"}
     25 
     26 var fieldReplacer = strings.NewReplacer(
     27 	"\n", "\\n",
     28 	"\r", "\\r")
     29 
     30 var dataReplacer = strings.NewReplacer(
     31 	"\n", "\ndata:",
     32 	"\r", "\\r")
     33 
     34 type Event struct {
     35 	Event string
     36 	Id    string
     37 	Retry uint
     38 	Data  interface{}
     39 }
     40 
     41 func Encode(writer io.Writer, event Event) error {
     42 	w := checkWriter(writer)
     43 	writeId(w, event.Id)
     44 	writeEvent(w, event.Event)
     45 	writeRetry(w, event.Retry)
     46 	return writeData(w, event.Data)
     47 }
     48 
     49 func writeId(w stringWriter, id string) {
     50 	if len(id) > 0 {
     51 		w.WriteString("id:")
     52 		fieldReplacer.WriteString(w, id)
     53 		w.WriteString("\n")
     54 	}
     55 }
     56 
     57 func writeEvent(w stringWriter, event string) {
     58 	if len(event) > 0 {
     59 		w.WriteString("event:")
     60 		fieldReplacer.WriteString(w, event)
     61 		w.WriteString("\n")
     62 	}
     63 }
     64 
     65 func writeRetry(w stringWriter, retry uint) {
     66 	if retry > 0 {
     67 		w.WriteString("retry:")
     68 		w.WriteString(strconv.FormatUint(uint64(retry), 10))
     69 		w.WriteString("\n")
     70 	}
     71 }
     72 
     73 func writeData(w stringWriter, data interface{}) error {
     74 	w.WriteString("data:")
     75 	switch kindOfData(data) {
     76 	case reflect.Struct, reflect.Slice, reflect.Map:
     77 		err := json.NewEncoder(w).Encode(data)
     78 		if err != nil {
     79 			return err
     80 		}
     81 		w.WriteString("\n")
     82 	default:
     83 		dataReplacer.WriteString(w, fmt.Sprint(data))
     84 		w.WriteString("\n\n")
     85 	}
     86 	return nil
     87 }
     88 
     89 func (r Event) Render(w http.ResponseWriter) error {
     90 	r.WriteContentType(w)
     91 	return Encode(w, r)
     92 }
     93 
     94 func (r Event) WriteContentType(w http.ResponseWriter) {
     95 	header := w.Header()
     96 	header["Content-Type"] = contentType
     97 
     98 	if _, exist := header["Cache-Control"]; !exist {
     99 		header["Cache-Control"] = noCache
    100 	}
    101 }
    102 
    103 func kindOfData(data interface{}) reflect.Kind {
    104 	value := reflect.ValueOf(data)
    105 	valueType := value.Kind()
    106 	if valueType == reflect.Ptr {
    107 		valueType = value.Elem().Kind()
    108 	}
    109 	return valueType
    110 }