mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
update vendor/ dir to latest w/o heroku, moby
had to lock a lot of things in place
This commit is contained in:
15
vendor/github.com/gin-contrib/sse/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/gin-contrib/sse/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.6.4
|
||||
- 1.7.4
|
||||
- tip
|
||||
|
||||
git:
|
||||
depth: 3
|
||||
|
||||
script:
|
||||
- go test -v -covermode=count -coverprofile=coverage.out
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
21
vendor/github.com/gin-contrib/sse/LICENSE
generated
vendored
Normal file
21
vendor/github.com/gin-contrib/sse/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Manuel Martínez-Almeida
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
58
vendor/github.com/gin-contrib/sse/README.md
generated
vendored
Normal file
58
vendor/github.com/gin-contrib/sse/README.md
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
# Server-Sent Events
|
||||
|
||||
[](https://godoc.org/github.com/gin-contrib/sse)
|
||||
[](https://travis-ci.org/gin-contrib/sse)
|
||||
[](https://codecov.io/gh/gin-contrib/sse)
|
||||
[](https://goreportcard.com/report/github.com/gin-contrib/sse)
|
||||
|
||||
Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is [standardized as part of HTML5[1] by the W3C](http://www.w3.org/TR/2009/WD-eventsource-20091029/).
|
||||
|
||||
- [Read this great SSE introduction by the HTML5Rocks guys](http://www.html5rocks.com/en/tutorials/eventsource/basics/)
|
||||
- [Browser support](http://caniuse.com/#feat=eventsource)
|
||||
|
||||
## Sample code
|
||||
|
||||
```go
|
||||
import "github.com/gin-contrib/sse"
|
||||
|
||||
func httpHandler(w http.ResponseWriter, req *http.Request) {
|
||||
// data can be a primitive like a string, an integer or a float
|
||||
sse.Encode(w, sse.Event{
|
||||
Event: "message",
|
||||
Data: "some data\nmore data",
|
||||
})
|
||||
|
||||
// also a complex type, like a map, a struct or a slice
|
||||
sse.Encode(w, sse.Event{
|
||||
Id: "124",
|
||||
Event: "message",
|
||||
Data: map[string]interface{}{
|
||||
"user": "manu",
|
||||
"date": time.Now().Unix(),
|
||||
"content": "hi!",
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
```
|
||||
event: message
|
||||
data: some data\\nmore data
|
||||
|
||||
id: 124
|
||||
event: message
|
||||
data: {"content":"hi!","date":1431540810,"user":"manu"}
|
||||
|
||||
```
|
||||
|
||||
## Content-Type
|
||||
|
||||
```go
|
||||
fmt.Println(sse.ContentType)
|
||||
```
|
||||
```
|
||||
text/event-stream
|
||||
```
|
||||
|
||||
## Decoding support
|
||||
|
||||
There is a client-side implementation of SSE coming soon.
|
||||
116
vendor/github.com/gin-contrib/sse/sse-decoder.go
generated
vendored
Normal file
116
vendor/github.com/gin-contrib/sse/sse-decoder.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type decoder struct {
|
||||
events []Event
|
||||
}
|
||||
|
||||
func Decode(r io.Reader) ([]Event, error) {
|
||||
var dec decoder
|
||||
return dec.decode(r)
|
||||
}
|
||||
|
||||
func (d *decoder) dispatchEvent(event Event, data string) {
|
||||
dataLength := len(data)
|
||||
if dataLength > 0 {
|
||||
//If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
|
||||
data = data[:dataLength-1]
|
||||
dataLength--
|
||||
}
|
||||
if dataLength == 0 && event.Event == "" {
|
||||
return
|
||||
}
|
||||
if event.Event == "" {
|
||||
event.Event = "message"
|
||||
}
|
||||
event.Data = data
|
||||
d.events = append(d.events, event)
|
||||
}
|
||||
|
||||
func (d *decoder) decode(r io.Reader) ([]Event, error) {
|
||||
buf, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var currentEvent Event
|
||||
var dataBuffer *bytes.Buffer = new(bytes.Buffer)
|
||||
// TODO (and unit tests)
|
||||
// Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair,
|
||||
// a single U+000A LINE FEED (LF) character,
|
||||
// or a single U+000D CARRIAGE RETURN (CR) character.
|
||||
lines := bytes.Split(buf, []byte{'\n'})
|
||||
for _, line := range lines {
|
||||
if len(line) == 0 {
|
||||
// If the line is empty (a blank line). Dispatch the event.
|
||||
d.dispatchEvent(currentEvent, dataBuffer.String())
|
||||
|
||||
// reset current event and data buffer
|
||||
currentEvent = Event{}
|
||||
dataBuffer.Reset()
|
||||
continue
|
||||
}
|
||||
if line[0] == byte(':') {
|
||||
// If the line starts with a U+003A COLON character (:), ignore the line.
|
||||
continue
|
||||
}
|
||||
|
||||
var field, value []byte
|
||||
colonIndex := bytes.IndexRune(line, ':')
|
||||
if colonIndex != -1 {
|
||||
// If the line contains a U+003A COLON character character (:)
|
||||
// Collect the characters on the line before the first U+003A COLON character (:),
|
||||
// and let field be that string.
|
||||
field = line[:colonIndex]
|
||||
// Collect the characters on the line after the first U+003A COLON character (:),
|
||||
// and let value be that string.
|
||||
value = line[colonIndex+1:]
|
||||
// If value starts with a single U+0020 SPACE character, remove it from value.
|
||||
if len(value) > 0 && value[0] == ' ' {
|
||||
value = value[1:]
|
||||
}
|
||||
} else {
|
||||
// Otherwise, the string is not empty but does not contain a U+003A COLON character character (:)
|
||||
// Use the whole line as the field name, and the empty string as the field value.
|
||||
field = line
|
||||
value = []byte{}
|
||||
}
|
||||
// The steps to process the field given a field name and a field value depend on the field name,
|
||||
// as given in the following list. Field names must be compared literally,
|
||||
// with no case folding performed.
|
||||
switch string(field) {
|
||||
case "event":
|
||||
// Set the event name buffer to field value.
|
||||
currentEvent.Event = string(value)
|
||||
case "id":
|
||||
// Set the event stream's last event ID to the field value.
|
||||
currentEvent.Id = string(value)
|
||||
case "retry":
|
||||
// If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
|
||||
// then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer.
|
||||
// Otherwise, ignore the field.
|
||||
currentEvent.Id = string(value)
|
||||
case "data":
|
||||
// Append the field value to the data buffer,
|
||||
dataBuffer.Write(value)
|
||||
// then append a single U+000A LINE FEED (LF) character to the data buffer.
|
||||
dataBuffer.WriteString("\n")
|
||||
default:
|
||||
//Otherwise. The field is ignored.
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Once the end of the file is reached, the user agent must dispatch the event one final time.
|
||||
d.dispatchEvent(currentEvent, dataBuffer.String())
|
||||
|
||||
return d.events, nil
|
||||
}
|
||||
116
vendor/github.com/gin-contrib/sse/sse-decoder_test.go
generated
vendored
Normal file
116
vendor/github.com/gin-contrib/sse/sse-decoder_test.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDecodeSingle1(t *testing.T) {
|
||||
events, err := Decode(bytes.NewBufferString(
|
||||
`data: this is a text
|
||||
event: message
|
||||
fake:
|
||||
id: 123456789010
|
||||
: we can append data
|
||||
: and multiple comments should not break it
|
||||
data: a very nice one`))
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, events, 1)
|
||||
assert.Equal(t, events[0].Event, "message")
|
||||
assert.Equal(t, events[0].Id, "123456789010")
|
||||
}
|
||||
|
||||
func TestDecodeSingle2(t *testing.T) {
|
||||
events, err := Decode(bytes.NewBufferString(
|
||||
`: starting with a comment
|
||||
fake:
|
||||
|
||||
data:this is a \ntext
|
||||
event:a message\n\n
|
||||
fake
|
||||
:and multiple comments\n should not break it\n\n
|
||||
id:1234567890\n10
|
||||
:we can append data
|
||||
data:a very nice one\n!
|
||||
|
||||
|
||||
`))
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, events, 1)
|
||||
assert.Equal(t, events[0].Event, "a message\\n\\n")
|
||||
assert.Equal(t, events[0].Id, "1234567890\\n10")
|
||||
}
|
||||
|
||||
func TestDecodeSingle3(t *testing.T) {
|
||||
events, err := Decode(bytes.NewBufferString(
|
||||
`
|
||||
id:123456ABCabc789010
|
||||
event: message123
|
||||
: we can append data
|
||||
data:this is a text
|
||||
data: a very nice one
|
||||
data:
|
||||
data
|
||||
: ending with a comment`))
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, events, 1)
|
||||
assert.Equal(t, events[0].Event, "message123")
|
||||
assert.Equal(t, events[0].Id, "123456ABCabc789010")
|
||||
}
|
||||
|
||||
func TestDecodeMulti1(t *testing.T) {
|
||||
events, err := Decode(bytes.NewBufferString(
|
||||
`
|
||||
id:
|
||||
event: weird event
|
||||
data:this is a text
|
||||
:data: this should NOT APER
|
||||
data: second line
|
||||
|
||||
: a comment
|
||||
event: message
|
||||
id:123
|
||||
data:this is a text
|
||||
:data: this should NOT APER
|
||||
data: second line
|
||||
|
||||
|
||||
: a comment
|
||||
event: message
|
||||
id:123
|
||||
data:this is a text
|
||||
data: second line
|
||||
|
||||
:hola
|
||||
|
||||
data
|
||||
|
||||
event:
|
||||
|
||||
id`))
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, events, 3)
|
||||
assert.Equal(t, events[0].Event, "weird event")
|
||||
assert.Equal(t, events[0].Id, "")
|
||||
}
|
||||
|
||||
func TestDecodeW3C(t *testing.T) {
|
||||
events, err := Decode(bytes.NewBufferString(
|
||||
`data
|
||||
|
||||
data
|
||||
data
|
||||
|
||||
data:
|
||||
`))
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, events, 1)
|
||||
}
|
||||
110
vendor/github.com/gin-contrib/sse/sse-encoder.go
generated
vendored
Normal file
110
vendor/github.com/gin-contrib/sse/sse-encoder.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sse
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Server-Sent Events
|
||||
// W3C Working Draft 29 October 2009
|
||||
// http://www.w3.org/TR/2009/WD-eventsource-20091029/
|
||||
|
||||
const ContentType = "text/event-stream"
|
||||
|
||||
var contentType = []string{ContentType}
|
||||
var noCache = []string{"no-cache"}
|
||||
|
||||
var fieldReplacer = strings.NewReplacer(
|
||||
"\n", "\\n",
|
||||
"\r", "\\r")
|
||||
|
||||
var dataReplacer = strings.NewReplacer(
|
||||
"\n", "\ndata:",
|
||||
"\r", "\\r")
|
||||
|
||||
type Event struct {
|
||||
Event string
|
||||
Id string
|
||||
Retry uint
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func Encode(writer io.Writer, event Event) error {
|
||||
w := checkWriter(writer)
|
||||
writeId(w, event.Id)
|
||||
writeEvent(w, event.Event)
|
||||
writeRetry(w, event.Retry)
|
||||
return writeData(w, event.Data)
|
||||
}
|
||||
|
||||
func writeId(w stringWriter, id string) {
|
||||
if len(id) > 0 {
|
||||
w.WriteString("id:")
|
||||
fieldReplacer.WriteString(w, id)
|
||||
w.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func writeEvent(w stringWriter, event string) {
|
||||
if len(event) > 0 {
|
||||
w.WriteString("event:")
|
||||
fieldReplacer.WriteString(w, event)
|
||||
w.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func writeRetry(w stringWriter, retry uint) {
|
||||
if retry > 0 {
|
||||
w.WriteString("retry:")
|
||||
w.WriteString(strconv.FormatUint(uint64(retry), 10))
|
||||
w.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func writeData(w stringWriter, data interface{}) error {
|
||||
w.WriteString("data:")
|
||||
switch kindOfData(data) {
|
||||
case reflect.Struct, reflect.Slice, reflect.Map:
|
||||
err := json.NewEncoder(w).Encode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteString("\n")
|
||||
default:
|
||||
dataReplacer.WriteString(w, fmt.Sprint(data))
|
||||
w.WriteString("\n\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Event) Render(w http.ResponseWriter) error {
|
||||
r.WriteContentType(w)
|
||||
return Encode(w, r)
|
||||
}
|
||||
|
||||
func (r Event) WriteContentType(w http.ResponseWriter) {
|
||||
header := w.Header()
|
||||
header["Content-Type"] = contentType
|
||||
|
||||
if _, exist := header["Cache-Control"]; !exist {
|
||||
header["Cache-Control"] = noCache
|
||||
}
|
||||
}
|
||||
|
||||
func kindOfData(data interface{}) reflect.Kind {
|
||||
value := reflect.ValueOf(data)
|
||||
valueType := value.Kind()
|
||||
if valueType == reflect.Ptr {
|
||||
valueType = value.Elem().Kind()
|
||||
}
|
||||
return valueType
|
||||
}
|
||||
255
vendor/github.com/gin-contrib/sse/sse_test.go
generated
vendored
Normal file
255
vendor/github.com/gin-contrib/sse/sse_test.go
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEncodeOnlyData(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
event := Event{
|
||||
Data: "junk\n\njk\nid:fake",
|
||||
}
|
||||
err := Encode(w, event)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(),
|
||||
`data:junk
|
||||
data:
|
||||
data:jk
|
||||
data:id:fake
|
||||
|
||||
`)
|
||||
|
||||
decoded, _ := Decode(w)
|
||||
assert.Equal(t, decoded, []Event{event})
|
||||
}
|
||||
|
||||
func TestEncodeWithEvent(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
event := Event{
|
||||
Event: "t\n:<>\r\test",
|
||||
Data: "junk\n\njk\nid:fake",
|
||||
}
|
||||
err := Encode(w, event)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(),
|
||||
`event:t\n:<>\r est
|
||||
data:junk
|
||||
data:
|
||||
data:jk
|
||||
data:id:fake
|
||||
|
||||
`)
|
||||
|
||||
decoded, _ := Decode(w)
|
||||
assert.Equal(t, decoded, []Event{event})
|
||||
}
|
||||
|
||||
func TestEncodeWithId(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Id: "t\n:<>\r\test",
|
||||
Data: "junk\n\njk\nid:fa\rke",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(),
|
||||
`id:t\n:<>\r est
|
||||
data:junk
|
||||
data:
|
||||
data:jk
|
||||
data:id:fa\rke
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
func TestEncodeWithRetry(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Retry: 11,
|
||||
Data: "junk\n\njk\nid:fake\n",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(),
|
||||
`retry:11
|
||||
data:junk
|
||||
data:
|
||||
data:jk
|
||||
data:id:fake
|
||||
data:
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
func TestEncodeWithEverything(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "abc",
|
||||
Id: "12345",
|
||||
Retry: 10,
|
||||
Data: "some data",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "id:12345\nevent:abc\nretry:10\ndata:some data\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeMap(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "a map",
|
||||
Data: map[string]interface{}{
|
||||
"foo": "b\n\rar",
|
||||
"bar": "id: 2",
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event:a map\ndata:{\"bar\":\"id: 2\",\"foo\":\"b\\n\\rar\"}\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeSlice(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "a slice",
|
||||
Data: []interface{}{1, "text", map[string]interface{}{"foo": "bar"}},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event:a slice\ndata:[1,\"text\",{\"foo\":\"bar\"}]\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeStruct(t *testing.T) {
|
||||
myStruct := struct {
|
||||
A int
|
||||
B string `json:"value"`
|
||||
}{1, "number"}
|
||||
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "a struct",
|
||||
Data: myStruct,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event:a struct\ndata:{\"A\":1,\"value\":\"number\"}\n\n")
|
||||
|
||||
w.Reset()
|
||||
err = Encode(w, Event{
|
||||
Event: "a struct",
|
||||
Data: &myStruct,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event:a struct\ndata:{\"A\":1,\"value\":\"number\"}\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeInteger(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "an integer",
|
||||
Data: 1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event:an integer\ndata:1\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeFloat(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
err := Encode(w, Event{
|
||||
Event: "Float",
|
||||
Data: 1.5,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.String(), "event:Float\ndata:1.5\n\n")
|
||||
}
|
||||
|
||||
func TestEncodeStream(t *testing.T) {
|
||||
w := new(bytes.Buffer)
|
||||
|
||||
Encode(w, Event{
|
||||
Event: "float",
|
||||
Data: 1.5,
|
||||
})
|
||||
|
||||
Encode(w, Event{
|
||||
Id: "123",
|
||||
Data: map[string]interface{}{"foo": "bar", "bar": "foo"},
|
||||
})
|
||||
|
||||
Encode(w, Event{
|
||||
Id: "124",
|
||||
Event: "chat",
|
||||
Data: "hi! dude",
|
||||
})
|
||||
assert.Equal(t, w.String(), "event:float\ndata:1.5\n\nid:123\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\nid:124\nevent:chat\ndata:hi! dude\n\n")
|
||||
}
|
||||
|
||||
func TestRenderSSE(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
err := (Event{
|
||||
Event: "msg",
|
||||
Data: "hi! how are you?",
|
||||
}).Render(w)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, w.Body.String(), "event:msg\ndata:hi! how are you?\n\n")
|
||||
assert.Equal(t, w.Header().Get("Content-Type"), "text/event-stream")
|
||||
assert.Equal(t, w.Header().Get("Cache-Control"), "no-cache")
|
||||
}
|
||||
|
||||
func BenchmarkResponseWriter(b *testing.B) {
|
||||
w := httptest.NewRecorder()
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
(Event{
|
||||
Event: "new_message",
|
||||
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
||||
}).Render(w)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFullSSE(b *testing.B) {
|
||||
buf := new(bytes.Buffer)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Encode(buf, Event{
|
||||
Event: "new_message",
|
||||
Id: "13435",
|
||||
Retry: 10,
|
||||
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
||||
})
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNoRetrySSE(b *testing.B) {
|
||||
buf := new(bytes.Buffer)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Encode(buf, Event{
|
||||
Event: "new_message",
|
||||
Id: "13435",
|
||||
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
||||
})
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSimpleSSE(b *testing.B) {
|
||||
buf := new(bytes.Buffer)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Encode(buf, Event{
|
||||
Event: "new_message",
|
||||
Data: "hi! how are you? I am fine. this is a long stupid message!!!",
|
||||
})
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
24
vendor/github.com/gin-contrib/sse/writer.go
generated
vendored
Normal file
24
vendor/github.com/gin-contrib/sse/writer.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package sse
|
||||
|
||||
import "io"
|
||||
|
||||
type stringWriter interface {
|
||||
io.Writer
|
||||
WriteString(string) (int, error)
|
||||
}
|
||||
|
||||
type stringWrapper struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (w stringWrapper) WriteString(str string) (int, error) {
|
||||
return w.Writer.Write([]byte(str))
|
||||
}
|
||||
|
||||
func checkWriter(writer io.Writer) stringWriter {
|
||||
if w, ok := writer.(stringWriter); ok {
|
||||
return w
|
||||
} else {
|
||||
return stringWrapper{writer}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user