Skip to content

Commit 214a93f

Browse files
committed
Use gin as HTTP server framework
1 parent d5beefa commit 214a93f

File tree

386 files changed

+144667
-38388
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

386 files changed

+144667
-38388
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
language: go
2+
sudo: false
3+
go:
4+
- 1.6.4
5+
- 1.7.4
6+
- tip
7+
8+
git:
9+
depth: 3
10+
11+
script:
12+
- go test -v -covermode=count -coverprofile=coverage.out
13+
14+
after_success:
15+
- bash <(curl -s https://codecov.io/bash)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2014 Manuel Martínez-Almeida
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Server-Sent Events
2+
3+
[![GoDoc](https://godoc.org/github.com/gin-contrib/sse?status.svg)](https://godoc.org/github.com/gin-contrib/sse)
4+
[![Build Status](https://travis-ci.org/gin-contrib/sse.svg)](https://travis-ci.org/gin-contrib/sse)
5+
[![codecov](https://codecov.io/gh/gin-contrib/sse/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-contrib/sse)
6+
[![Go Report Card](https://goreportcard.com/badge/github.com/gin-contrib/sse)](https://goreportcard.com/report/github.com/gin-contrib/sse)
7+
8+
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/).
9+
10+
- [Read this great SSE introduction by the HTML5Rocks guys](http://www.html5rocks.com/en/tutorials/eventsource/basics/)
11+
- [Browser support](http://caniuse.com/#feat=eventsource)
12+
13+
## Sample code
14+
15+
```go
16+
import "github.com/gin-contrib/sse"
17+
18+
func httpHandler(w http.ResponseWriter, req *http.Request) {
19+
// data can be a primitive like a string, an integer or a float
20+
sse.Encode(w, sse.Event{
21+
Event: "message",
22+
Data: "some data\nmore data",
23+
})
24+
25+
// also a complex type, like a map, a struct or a slice
26+
sse.Encode(w, sse.Event{
27+
Id: "124",
28+
Event: "message",
29+
Data: map[string]interface{}{
30+
"user": "manu",
31+
"date": time.Now().Unix(),
32+
"content": "hi!",
33+
},
34+
})
35+
}
36+
```
37+
```
38+
event: message
39+
data: some data\\nmore data
40+
41+
id: 124
42+
event: message
43+
data: {"content":"hi!","date":1431540810,"user":"manu"}
44+
45+
```
46+
47+
## Content-Type
48+
49+
```go
50+
fmt.Println(sse.ContentType)
51+
```
52+
```
53+
text/event-stream
54+
```
55+
56+
## Decoding support
57+
58+
There is a client-side implementation of SSE coming soon.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
"bytes"
9+
"io"
10+
"io/ioutil"
11+
)
12+
13+
type decoder struct {
14+
events []Event
15+
}
16+
17+
func Decode(r io.Reader) ([]Event, error) {
18+
var dec decoder
19+
return dec.decode(r)
20+
}
21+
22+
func (d *decoder) dispatchEvent(event Event, data string) {
23+
dataLength := len(data)
24+
if dataLength > 0 {
25+
//If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
26+
data = data[:dataLength-1]
27+
dataLength--
28+
}
29+
if dataLength == 0 && event.Event == "" {
30+
return
31+
}
32+
if event.Event == "" {
33+
event.Event = "message"
34+
}
35+
event.Data = data
36+
d.events = append(d.events, event)
37+
}
38+
39+
func (d *decoder) decode(r io.Reader) ([]Event, error) {
40+
buf, err := ioutil.ReadAll(r)
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
var currentEvent Event
46+
var dataBuffer *bytes.Buffer = new(bytes.Buffer)
47+
// TODO (and unit tests)
48+
// Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair,
49+
// a single U+000A LINE FEED (LF) character,
50+
// or a single U+000D CARRIAGE RETURN (CR) character.
51+
lines := bytes.Split(buf, []byte{'\n'})
52+
for _, line := range lines {
53+
if len(line) == 0 {
54+
// If the line is empty (a blank line). Dispatch the event.
55+
d.dispatchEvent(currentEvent, dataBuffer.String())
56+
57+
// reset current event and data buffer
58+
currentEvent = Event{}
59+
dataBuffer.Reset()
60+
continue
61+
}
62+
if line[0] == byte(':') {
63+
// If the line starts with a U+003A COLON character (:), ignore the line.
64+
continue
65+
}
66+
67+
var field, value []byte
68+
colonIndex := bytes.IndexRune(line, ':')
69+
if colonIndex != -1 {
70+
// If the line contains a U+003A COLON character character (:)
71+
// Collect the characters on the line before the first U+003A COLON character (:),
72+
// and let field be that string.
73+
field = line[:colonIndex]
74+
// Collect the characters on the line after the first U+003A COLON character (:),
75+
// and let value be that string.
76+
value = line[colonIndex+1:]
77+
// If value starts with a single U+0020 SPACE character, remove it from value.
78+
if len(value) > 0 && value[0] == ' ' {
79+
value = value[1:]
80+
}
81+
} else {
82+
// Otherwise, the string is not empty but does not contain a U+003A COLON character character (:)
83+
// Use the whole line as the field name, and the empty string as the field value.
84+
field = line
85+
value = []byte{}
86+
}
87+
// The steps to process the field given a field name and a field value depend on the field name,
88+
// as given in the following list. Field names must be compared literally,
89+
// with no case folding performed.
90+
switch string(field) {
91+
case "event":
92+
// Set the event name buffer to field value.
93+
currentEvent.Event = string(value)
94+
case "id":
95+
// Set the event stream's last event ID to the field value.
96+
currentEvent.Id = string(value)
97+
case "retry":
98+
// If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
99+
// then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer.
100+
// Otherwise, ignore the field.
101+
currentEvent.Id = string(value)
102+
case "data":
103+
// Append the field value to the data buffer,
104+
dataBuffer.Write(value)
105+
// then append a single U+000A LINE FEED (LF) character to the data buffer.
106+
dataBuffer.WriteString("\n")
107+
default:
108+
//Otherwise. The field is ignored.
109+
continue
110+
}
111+
}
112+
// Once the end of the file is reached, the user agent must dispatch the event one final time.
113+
d.dispatchEvent(currentEvent, dataBuffer.String())
114+
115+
return d.events, nil
116+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
"bytes"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func TestDecodeSingle1(t *testing.T) {
15+
events, err := Decode(bytes.NewBufferString(
16+
`data: this is a text
17+
event: message
18+
fake:
19+
id: 123456789010
20+
: we can append data
21+
: and multiple comments should not break it
22+
data: a very nice one`))
23+
24+
assert.NoError(t, err)
25+
assert.Len(t, events, 1)
26+
assert.Equal(t, events[0].Event, "message")
27+
assert.Equal(t, events[0].Id, "123456789010")
28+
}
29+
30+
func TestDecodeSingle2(t *testing.T) {
31+
events, err := Decode(bytes.NewBufferString(
32+
`: starting with a comment
33+
fake:
34+
35+
data:this is a \ntext
36+
event:a message\n\n
37+
fake
38+
:and multiple comments\n should not break it\n\n
39+
id:1234567890\n10
40+
:we can append data
41+
data:a very nice one\n!
42+
43+
44+
`))
45+
assert.NoError(t, err)
46+
assert.Len(t, events, 1)
47+
assert.Equal(t, events[0].Event, "a message\\n\\n")
48+
assert.Equal(t, events[0].Id, "1234567890\\n10")
49+
}
50+
51+
func TestDecodeSingle3(t *testing.T) {
52+
events, err := Decode(bytes.NewBufferString(
53+
`
54+
id:123456ABCabc789010
55+
event: message123
56+
: we can append data
57+
data:this is a text
58+
data: a very nice one
59+
data:
60+
data
61+
: ending with a comment`))
62+
63+
assert.NoError(t, err)
64+
assert.Len(t, events, 1)
65+
assert.Equal(t, events[0].Event, "message123")
66+
assert.Equal(t, events[0].Id, "123456ABCabc789010")
67+
}
68+
69+
func TestDecodeMulti1(t *testing.T) {
70+
events, err := Decode(bytes.NewBufferString(
71+
`
72+
id:
73+
event: weird event
74+
data:this is a text
75+
:data: this should NOT APER
76+
data: second line
77+
78+
: a comment
79+
event: message
80+
id:123
81+
data:this is a text
82+
:data: this should NOT APER
83+
data: second line
84+
85+
86+
: a comment
87+
event: message
88+
id:123
89+
data:this is a text
90+
data: second line
91+
92+
:hola
93+
94+
data
95+
96+
event:
97+
98+
id`))
99+
assert.NoError(t, err)
100+
assert.Len(t, events, 3)
101+
assert.Equal(t, events[0].Event, "weird event")
102+
assert.Equal(t, events[0].Id, "")
103+
}
104+
105+
func TestDecodeW3C(t *testing.T) {
106+
events, err := Decode(bytes.NewBufferString(
107+
`data
108+
109+
data
110+
data
111+
112+
data:
113+
`))
114+
assert.NoError(t, err)
115+
assert.Len(t, events, 1)
116+
}

0 commit comments

Comments
 (0)