build: remove dep meta files, readd vendor directory

This commit is contained in:
mappu 2018-12-31 18:22:33 +13:00
parent a74d57e7fc
commit 1c6e3b1c9d
61 changed files with 15 additions and 21485 deletions

39
Gopkg.lock generated
View File

@ -1,39 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "code.ivysaur.me/libnmdc"
packages = ["."]
revision = "a23cc9e61d24bb9b16b1126bdddd514e01d64792"
version = "v0.16.0"
[[projects]]
branch = "master"
name = "github.com/cxmcc/tiger"
packages = ["."]
revision = "bde35e2713d7f674987c2ecb21a6b0fc33749516"
[[projects]]
branch = "master"
name = "github.com/googollee/go-engine.io"
packages = [".","message","parser","polling","transport","websocket"]
revision = "80ae0e43aca17b4c5a6834999d0f2eaa16b9afda"
[[projects]]
branch = "master"
name = "github.com/googollee/go-socket.io"
packages = ["."]
revision = "5447e71f36d394766bf855d5714a487596809f0d"
[[projects]]
name = "github.com/gorilla/websocket"
packages = ["."]
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "7ac2438222289730d49b803fd1c631629921f95fd406c3154a54d34e11fb6947"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,30 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "code.ivysaur.me/libnmdc"
version = "0.16.0"
[[constraint]]
branch = "master"
name = "github.com/googollee/go-socket.io"

View File

@ -1,58 +0,0 @@
package libnmdc
import (
"fmt"
)
func ExampleHubConnectionOptions_Connect() {
opts := HubConnectionOptions{
Address: "127.0.0.1",
Self: NewUserInfo("slowpoke9"),
}
events := make(chan HubEvent, 0)
hub := ConnectAsync(&opts, events)
for event := range events {
switch event.EventType {
case EVENT_CONNECTION_STATE_CHANGED:
fmt.Printf("Connection -- %s (%s)\n", event.StateChange, event.Message)
case EVENT_PUBLIC:
fmt.Printf("Message from '%s': '%s'\n", event.Nick, event.Message)
if event.Message == "how are you" {
hub.SayPublic("good thanks!")
}
default:
fmt.Printf("%+v\n", event)
}
}
}
func ExampleHubConnectionOptions_ConnectSync() {
cb := func(hub *HubConnection, event HubEvent) {
switch event.EventType {
case EVENT_CONNECTION_STATE_CHANGED:
fmt.Printf("Connection -- %s (%s)\n", event.StateChange, event.Message)
case EVENT_PUBLIC:
fmt.Printf("Message from '%s': '%s'\n", event.Nick, event.Message)
if event.Message == "how are you" {
hub.SayPublic("good thanks!")
}
default:
fmt.Printf("%+v\n", event)
}
}
opts := HubConnectionOptions{
Address: "127.0.0.1",
Self: NewUserInfo("slowpoke9"),
}
ConnectSync(&opts, cb) // blocking
}

View File

@ -1,97 +0,0 @@
package libnmdc
import (
"testing"
)
func TestMyINFOParse(t *testing.T) {
np := NewNmdcProtocol(nil).(*NmdcProtocol)
type myInfoTestPair struct {
in string
expect UserInfo
}
cases := []myInfoTestPair{
myInfoTestPair{
in: "$ALL Bxxxy description<ApexDC++ V:1.4.3,M:P,H:9/0/2,S:1>$ $0.01\x01$xyz@example.com$53054999578$",
expect: UserInfo{
Nick: "Bxxxy",
Description: "description",
ClientTag: "ApexDC++",
ClientVersion: "1.4.3",
Email: "xyz@example.com",
ShareSize: 53054999578,
Flag: FLAG_NORMAL,
Slots: 1,
HubsUnregistered: 9,
HubsRegistered: 0,
HubsOperator: 2,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
Speed: "0.0",
},
},
},
myInfoTestPair{
in: "$ALL ixxxxxxx0 $P$10A$$0$",
expect: UserInfo{
Nick: "ixxxxxxx0",
ClientVersion: "0", // Auto-inserted by the parser for short-format MyINFO strings
Flag: UserFlag(rune('A')),
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
Speed: "1",
},
},
},
myInfoTestPair{
in: "$ALL SXXXX_XXXXXXR <ncdc V:1.19.1-12-g5561,M:P,H:1/0/0,S:10>$ $0.005Q$$0$",
expect: UserInfo{
Nick: "SXXXX_XXXXXXR",
ClientTag: "ncdc",
ClientVersion: "1.19.1-12-g5561",
Flag: UserFlag(rune('Q')),
Slots: 10,
HubsUnregistered: 1,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
Speed: "0.00",
},
},
},
myInfoTestPair{
in: "$ALL mxxxu desccccc<HexChat V:2.12.1,M:P,H:1/0/0,S:0>$ $p$$0$",
expect: UserInfo{
Nick: "mxxxu",
Description: "desccccc",
ClientTag: "HexChat",
ClientVersion: "2.12.1",
Flag: UserFlag(rune('p')),
HubsUnregistered: 1,
Slots: 0,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
},
},
},
}
for _, v := range cases {
got, err := np.parseMyINFO(v.in)
if err != nil {
t.Errorf("MyINFO parse warning (%s)", err.Error())
continue
}
if *got != v.expect {
t.Errorf("MyINFO parse failure\nExpected:\n%+v\nGot:\n%+v\n", v.expect, got)
continue
}
}
}

View File

@ -1,98 +0,0 @@
An NMDC / ADC client protocol library for Golang.
Written in golang
Tags: nmdc
=FEATURES=
- Connect to NMDC and ADC hubs
- SSL (NMDCS/ADCS) with option to ignore certificate validity
- Autodetect NMDC/ADC protocol by timeout
- Send public and private chat messages, UserCommand support
- Protocol keepalives
- Parse user details (including UserIP2 for NMDC)
- Fast NMDC login via NoHello and QuickList
- Both synchronous (callback) and asynchronous (channel) -based APIs, including example
=GO GET=
This package can be installed via go get: `go get code.ivysaur.me/libnmdc`
[go-get]code.ivysaur.me/libnmdc git https://git.ivysaur.me/code.ivysaur.me/libnmdc.git[/go-get]
=CHANGELOG=
2017-11-26 0.16
- Feature: Support connecting to ADC hubs
- BREAKING: Simplify connection API
- Vendor new dependency on github.com/cxmcc/tiger (MIT license)
2017-11-14 0.15
- Feature: Fallback reconnection if no data (not even keepalives) are recieved from the hub in 24 hours
- Fix an issue with detecting protocol messages inside multi-line chat messages
- Update examples and the default client version number
2017-02-09 0.14
- Fix an issue with crashing on malformed IP addresses supplied by the hub
2017-02-09 0.13
- Feature: Implement UserIP2 extension, to retrieve IP addresses of other users
- Enhancement: Implement QuickList extension (reduce one network roundtrip during initial connection)
- Enhancement: Implement NoHello extension (faster connection performance)
- Enhancement: Implement ChatOnly extension
- Fix an issue with not notifying client on all MyINFO updates
2017-02-05 0.12
- Fix an issue with mutex deadlock when accessing user information from a callback
- Fix an issue with silent disconnection if a password was required but not present
2016-11-29 0.11
- BREAKING: Remove some exported methods
- BREAKING: Fix an issue with missing sufficient parameters in the synchronous API
- Enhancement: Improve output under godoc
- Fix an issue with special characters appearing in recieved private messages
- Fix an issue with parsing active/passive connection modes
- Fix an issue with errors appearing on stdout
2016-10-08 r10
- Feature: Support `$UserCommand`
2016-08-27 r9
- Fix an issue with parsing MyINFO strings with zero-length speed descriptions
- Fix an issue with not storing updated profile information
2016-05-10 r8
- Enhancement: Separate `ClientTag` and `ClientVersion` in `UserInfo` structs
2016-05-08 r7
- BREAKING: Remove direct access to `HubConnection.Users` map
- Feature: Threadsafe user map accessor
- Feature: Option to disable auto-reconnection
- Feature: New `Disconnect()`, `UserCount()`, `UserExists()` functions
- Enhancement: Support `$OpList`, add `IsOperator` member to `UserInfo` structs
- Refactor into multiple files
2016-04-16 r6
- Fix an issue with calling `panic()` on certain types of abnormal network failure
2016-04-04 r5
- Enhancement: Support protocol keepalives
- Enhancement: Support hub redirects (`$ForceMove`)
2016-04-03 r4
- Feature: Add synchronous API
- Fix an issue with reading HubConnection's state parameter
- Fix an issue with buffered protocol commands
2016-04-03 r3
- Feature: Add `SkipVerifyTLS` option
- Fix an issue with calling `panic()` if connection failed
2016-04-02 r2
- Enhancement: Support NMDC-over-TLS (NMDCS)
- Fix an issue recieving private messages
- Fix an issue with calling `panic()` if connection failed
- Fix an issue parsing URIs without a specified port
- Move sample content into directory with excluded build
2016-02-12 r1
- Initial public release

View File

@ -1,36 +0,0 @@
package libnmdc
import (
"strings"
"testing"
)
func TestTTH(t *testing.T) {
// echo -n 'hello world' | tthsum
testCases := [][2]string{
[2]string{"hello world", "ZIIVRZDR2FD3W4KKNMNYUU3765LPPK7BWY64CHI"},
[2]string{"", "LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ"},
[2]string{"\x00", "VK54ZIEEVTWNAUI5D5RDFIL37LX2IQNSTAXFKSA"},
[2]string{strings.Repeat("A", 1024), "L66Q4YVNAFWVS23X2HJIRA5ZJ7WXR3F26RSASFA"},
}
short := func(s string) string {
if len(s) > 15 {
return s[0:15] + "..."
}
return s
}
for _, testCase := range testCases {
input, expected := testCase[0], testCase[1]
result, err := TTH(input)
if err != nil {
t.Fatalf("Error getting TTH for '%s': %s", short(input), err.Error())
}
if Base32(result) != expected {
t.Fatalf("Wrong TTH for '%s' (got '%s' expected '%s')", short(input), result, expected)
}
}
}

View File

@ -1,144 +0,0 @@
package tiger
import (
"fmt"
"io"
"strings"
"testing"
"unsafe"
)
type Test struct {
out string
in string
}
var golden = []Test{
{"3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3", ""},
{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f5f247809", "a"},
{"2aab1484e8c158f2bfb8c5ff41b57a525129131c957b5f93", "abc"},
{"d981f8cb78201a950dcf3048751e441c517fca1aa55a29f6", "message digest"},
{"1714a472eee57d30040412bfcc55032a0b11602ff37beee9", "abcdefghijklmnopqrstuvwxyz"},
{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e7b53f78e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
{"8dcea680a17583ee502ba38a3c368651890ffbccdc49a8cc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
{"1c14795529fd9f207a958f84c52f11e887fa0cabdfd91bfd", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
{"cdf0990c5c6b6b0bddd63a75ed20e2d448bf44e15fde0df4", strings.Repeat("A", 1024)},
{"89292aee0f82842abc080c57b3aadd9ca84d66bf0cae77aa", strings.Repeat("A", 1025)},
}
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
c := New()
buf := make([]byte, len(g.in)+4)
for j := 0; j < 7; j++ {
if j < 2 {
io.WriteString(c, g.in)
} else if j == 2 {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
} else if j > 2 {
// test unaligned write
buf = buf[1:]
copy(buf, g.in)
c.Write(buf[:len(g.in)])
}
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("tiger[%d](%s) = %s want %s", j, g.in, s, g.out)
}
c.Reset()
}
}
}
type WriteTest struct {
out int
in string
}
var writeTestVectors = []WriteTest{
{0, ""},
{1, "A"},
{2, "AA"},
{10, strings.Repeat("A", 10)},
{1024, strings.Repeat("A", 1024)},
{1025, strings.Repeat("A", 1025)},
{0, ""},
}
func TestWriteReturnsCorrectSize(t *testing.T) {
c := New()
for i := 0; i < len(writeTestVectors); i++ {
v := writeTestVectors[i]
b := []byte(v.in)
length, err := c.Write(b[:len(v.in)])
if length != v.out {
t.Fatalf("Write() = %d want %d", length, v.out)
}
if err != nil {
t.Fatalf("Write(%s) failed.", v.in)
}
}
}
func ExampleNew() {
h := New()
io.WriteString(h, "It's the eye of the tiger, it's the thrill of the fight")
io.WriteString(h, "Rising up to the challenge of our rival!")
fmt.Printf("%x", h.Sum(nil))
// Output: a7bbad36cc17918e399ae8ee893e4595e4d24e1639fe822c
}
func ExampleNew2() {
h := New2()
io.WriteString(h, "It's the eye of the tiger, it's the thrill of the fight")
io.WriteString(h, "Rising up to the challenge of our rival!")
fmt.Printf("%x", h.Sum(nil))
// Output: c86695c2a639506682de2c12c2d23b61a12db78ea1ee1001
}
var bench = New()
var buf = make([]byte, 8192+1)
var sum = make([]byte, bench.Size())
func benchmarkSize(b *testing.B, size int, unaligned bool) {
b.SetBytes(int64(size))
buf := buf
if unaligned {
if uintptr(unsafe.Pointer(&buf[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
buf = buf[1:]
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
bench.Reset()
bench.Write(buf[:size])
bench.Sum(sum[:0])
}
}
func BenchmarkHash8Bytes(b *testing.B) {
benchmarkSize(b, 8, false)
}
func BenchmarkHash1K(b *testing.B) {
benchmarkSize(b, 1024, false)
}
func BenchmarkHash8K(b *testing.B) {
benchmarkSize(b, 8192, false)
}
func BenchmarkHash8BytesUnaligned(b *testing.B) {
benchmarkSize(b, 8, true)
}
func BenchmarkHash1KUnaligned(b *testing.B) {
benchmarkSize(b, 1024, true)
}
func BenchmarkHash8KUnaligned(b *testing.B) {
benchmarkSize(b, 8192, true)
}

View File

@ -1,14 +0,0 @@
<!doctype html>
<html>
<head>
<title>EIO Latency</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<h1>EIO Latency <span id="latency"></span></h1>
<h2 id="transport">(connecting)</h2>
<canvas id="chart" height="200"></canvas>
<script src="/index.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
body { margin: 0; padding: 0; font-family: Helvetica Neue; }
h1 { margin: 100px 100px 10px; }
h2 { color: #999; margin: 0 100px 30px; font-weight: normal; }
#latency { color: red; }

View File

@ -1,60 +0,0 @@
package main
import (
"encoding/hex"
"io/ioutil"
"log"
"net/http"
"time"
"github.com/googollee/go-engine.io"
)
func main() {
server, err := engineio.NewServer(nil)
if err != nil {
log.Fatal(err)
}
server.SetPingInterval(time.Second * 2)
server.SetPingTimeout(time.Second * 3)
go func() {
for {
conn, _ := server.Accept()
go func() {
log.Println("connected:", conn.Id())
defer func() {
conn.Close()
log.Println("disconnected:", conn.Id())
}()
for {
t, r, err := conn.NextReader()
if err != nil {
return
}
b, err := ioutil.ReadAll(r)
if err != nil {
return
}
r.Close()
if t == engineio.MessageText {
log.Println(t, string(b))
} else {
log.Println(t, hex.EncodeToString(b))
}
w, err := conn.NextWriter(t)
if err != nil {
return
}
w.Write([]byte("pong"))
w.Close()
}
}()
}
}()
http.Handle("/engine.io/", server)
http.Handle("/", http.FileServer(http.Dir("./asset")))
log.Println("Serving at localhost:4000...")
log.Fatal(http.ListenAndServe(":4000", nil))
}

View File

@ -1,123 +0,0 @@
package engineio
import (
"bytes"
"github.com/googollee/go-engine.io/parser"
"io"
"sync"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func TestConnIoutil(t *testing.T) {
Convey("Reader", t, func() {
Convey("Normal read", func() {
r := bytes.NewBufferString("\x34\xe6\xb5\x8b\xe8\xaf\x95")
decoder, err := parser.NewDecoder(r)
So(err, ShouldBeNil)
closeChan := make(chan struct{})
reader := newConnReader(decoder, closeChan)
b := make([]byte, 1024)
n, err := reader.Read(b)
So(err, ShouldBeNil)
So(string(b[:n]), ShouldEqual, "测试")
n, err = reader.Read(b)
So(err, ShouldEqual, io.EOF)
Convey("Wait close", func() {
check := make(chan int)
go func() {
err := reader.Close()
if err != nil {
t.Fatal(err)
}
check <- 1
}()
time.Sleep(time.Second / 10) // wait goroutine start
select {
case <-check:
So("should not run here", ShouldEqual, "")
default:
}
<-closeChan
time.Sleep(time.Second / 10) // wait goroutine end
select {
case <-check:
default:
So("should not run here", ShouldEqual, "")
}
Convey("Close again", func() {
err := reader.Close()
So(err, ShouldBeNil)
})
})
})
})
Convey("Wrtier", t, func() {
Convey("Normal write", func() {
locker := sync.Mutex{}
w := bytes.NewBuffer(nil)
locker.Lock()
writer := newConnWriter(writeCloser{w}, &locker)
_, err := writer.Write([]byte("abc"))
So(err, ShouldBeNil)
So(w.String(), ShouldEqual, "abc")
writer.Close()
})
Convey("Sync", func() {
locker := sync.Mutex{}
w1 := bytes.NewBuffer(nil)
locker.Lock()
writer1 := newConnWriter(writeCloser{w1}, &locker)
check := make(chan int)
go func() {
w2 := bytes.NewBuffer(nil)
locker.Lock()
writer2 := newConnWriter(writeCloser{w2}, &locker)
defer writer2.Close()
check <- 1
}()
time.Sleep(time.Second / 10)
select {
case <-check:
So("should not run here", ShouldEqual, "")
default:
}
err := writer1.Close()
So(err, ShouldBeNil)
time.Sleep(time.Second / 10) // wait goroutine end
select {
case <-check:
default:
So("should not run here", ShouldEqual, "")
}
Convey("Close again", func() {
err := writer1.Close()
So(err, ShouldBeNil)
})
})
})
}
type writeCloser struct {
io.Writer
}
func (w writeCloser) Close() error {
return nil
}

View File

@ -1,58 +0,0 @@
package parser
import (
"bytes"
"errors"
"io"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestLimitReader(t *testing.T) {
Convey("Read to limit", t, func() {
b := bytes.NewBufferString("1234567890")
r := newLimitReader(b, 5)
p := make([]byte, 1024)
n, err := r.Read(p)
So(err, ShouldBeNil)
So(string(p[:n]), ShouldEqual, "12345")
n, err = r.Read(p)
So(err, ShouldEqual, io.EOF)
err = r.Close()
So(err, ShouldBeNil)
So(b.String(), ShouldEqual, "67890")
})
Convey("Read some and close", t, func() {
b := bytes.NewBufferString("1234567890")
r := newLimitReader(b, 5)
p := make([]byte, 3)
n, err := r.Read(p)
So(err, ShouldBeNil)
So(string(p[:n]), ShouldEqual, "123")
err = r.Close()
So(err, ShouldBeNil)
So(b.String(), ShouldEqual, "67890")
err = r.Close()
So(err, ShouldBeNil)
})
Convey("Close with error", t, func() {
er := errorReader{}
r := newLimitReader(er, 5)
err := r.Close()
So(err, ShouldNotBeNil)
})
}
type errorReader struct{}
func (r errorReader) Read(p []byte) (int, error) {
return 0, errors.New("error")
}
func (r errorReader) Close() error {
return errors.New("error")
}

View File

@ -1,297 +0,0 @@
package parser
import (
"bytes"
"io"
"testing"
"github.com/googollee/go-engine.io/message"
. "github.com/smartystreets/goconvey/convey"
)
func TestPacketType(t *testing.T) {
Convey("Byte to type", t, func() {
Convey("Open", func() {
t, err := ByteToType(0)
So(err, ShouldBeNil)
So(t, ShouldEqual, OPEN)
})
Convey("Close", func() {
t, err := ByteToType(1)
So(err, ShouldBeNil)
So(t, ShouldEqual, CLOSE)
})
Convey("Ping", func() {
t, err := ByteToType(2)
So(err, ShouldBeNil)
So(t, ShouldEqual, PING)
})
Convey("Pong", func() {
t, err := ByteToType(3)
So(err, ShouldBeNil)
So(t, ShouldEqual, PONG)
})
Convey("Message", func() {
t, err := ByteToType(4)
So(err, ShouldBeNil)
So(t, ShouldEqual, MESSAGE)
})
Convey("Upgrade", func() {
t, err := ByteToType(5)
So(err, ShouldBeNil)
So(t, ShouldEqual, UPGRADE)
})
Convey("Noop", func() {
t, err := ByteToType(6)
So(err, ShouldBeNil)
So(t, ShouldEqual, NOOP)
})
Convey("Error", func() {
_, err := ByteToType(7)
So(err, ShouldNotBeNil)
})
})
Convey("Type to byte", t, func() {
Convey("Open", func() {
So(OPEN.Byte(), ShouldEqual, 0)
})
Convey("Close", func() {
So(CLOSE.Byte(), ShouldEqual, 1)
})
Convey("Ping", func() {
So(PING.Byte(), ShouldEqual, 2)
})
Convey("Pong", func() {
So(PONG.Byte(), ShouldEqual, 3)
})
Convey("Message", func() {
So(MESSAGE.Byte(), ShouldEqual, 4)
})
Convey("Upgrade", func() {
So(UPGRADE.Byte(), ShouldEqual, 5)
})
Convey("Noop", func() {
So(NOOP.Byte(), ShouldEqual, 6)
})
})
}
func TestStringParser(t *testing.T) {
type Test struct {
name string
t PacketType
data []byte
output string
}
var tests = []Test{
{"without data", OPEN, nil, "0"},
{"with data", MESSAGE, []byte("测试"), "\x34\xe6\xb5\x8b\xe8\xaf\x95"},
}
for _, test := range tests {
buf := bytes.NewBuffer(nil)
Convey("Given a packet type "+test.name, t, func() {
Convey("Create encoder", func() {
encoder, err := NewStringEncoder(buf, test.t)
So(err, ShouldBeNil)
So(encoder, ShouldImplement, (*io.WriteCloser)(nil))
Convey("Encoded", func() {
for d := test.data; len(d) > 0; {
n, err := encoder.Write(d)
So(err, ShouldBeNil)
d = d[n:]
}
Convey("End", func() {
err := encoder.Close()
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, test.output)
})
})
})
Convey("Create decoder", func() {
decoder, err := NewDecoder(buf)
So(err, ShouldBeNil)
So(decoder, ShouldImplement, (*io.ReadCloser)(nil))
So(decoder.MessageType(), ShouldEqual, message.MessageText)
Convey("Decoded", func() {
So(decoder.Type(), ShouldEqual, test.t)
decoded := make([]byte, len(test.data)+1)
n, err := decoder.Read(decoded)
if n > 0 {
So(err, ShouldBeNil)
So(decoded[:n], ShouldResemble, test.data)
}
Convey("End", func() {
_, err := decoder.Read(decoded[:])
So(err, ShouldEqual, io.EOF)
})
})
})
})
}
}
func TestBinaryParser(t *testing.T) {
type Test struct {
name string
t PacketType
data []byte
output string
}
var tests = []Test{
{"without data", OPEN, nil, "\x00"},
{"with data", MESSAGE, []byte("测试"), "\x04\xe6\xb5\x8b\xe8\xaf\x95"},
}
for _, test := range tests {
buf := bytes.NewBuffer(nil)
Convey("Given a packet type "+test.name, t, func() {
Convey("Create Encoder", func() {
encoder, err := NewBinaryEncoder(buf, test.t)
So(err, ShouldBeNil)
So(encoder, ShouldImplement, (*io.WriteCloser)(nil))
Convey("Encoded", func() {
for d := test.data; len(d) > 0; {
n, err := encoder.Write(d)
So(err, ShouldBeNil)
d = d[n:]
}
Convey("End", func() {
err := encoder.Close()
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, test.output)
})
})
})
Convey("Create decoder", func() {
decoder, err := NewDecoder(buf)
So(err, ShouldBeNil)
So(decoder, ShouldImplement, (*io.ReadCloser)(nil))
So(decoder.MessageType(), ShouldEqual, message.MessageBinary)
Convey("Decoded", func() {
So(decoder.Type(), ShouldEqual, test.t)
decoded := make([]byte, len(test.data)+1)
n, err := decoder.Read(decoded[:])
if n > 0 {
So(err, ShouldBeNil)
So(decoded[:n], ShouldResemble, test.data)
}
Convey("End", func() {
_, err := decoder.Read(decoded[:])
So(err, ShouldEqual, io.EOF)
})
})
})
})
}
}
func TestBase64Parser(t *testing.T) {
type Test struct {
name string
t PacketType
data []byte
output string
}
var tests = []Test{
{"without data", OPEN, nil, "b0"},
{"with data", MESSAGE, []byte("测试"), "b45rWL6K+V"},
}
for _, test := range tests {
buf := bytes.NewBuffer(nil)
Convey("Given a packet type "+test.name, t, func() {
Convey("Create Encoder", func() {
encoder, err := NewB64Encoder(buf, test.t)
So(err, ShouldBeNil)
So(encoder, ShouldImplement, (*io.WriteCloser)(nil))
Convey("Encoded", func() {
for d := test.data; len(d) > 0; {
n, err := encoder.Write(d)
So(err, ShouldBeNil)
d = d[n:]
}
Convey("End", func() {
err := encoder.Close()
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, test.output)
})
})
})
Convey("Create decoder", func() {
decoder, err := NewDecoder(buf)
So(err, ShouldBeNil)
So(decoder, ShouldImplement, (*io.ReadCloser)(nil))
So(decoder.MessageType(), ShouldEqual, message.MessageBinary)
Convey("Decoded", func() {
So(decoder.Type(), ShouldEqual, test.t)
decoded := make([]byte, len(test.data)+1)
n, err := decoder.Read(decoded[:])
if n > 0 {
So(err, ShouldBeNil)
So(decoded[:n], ShouldResemble, test.data)
}
Convey("End", func() {
_, err := decoder.Read(decoded[:])
So(err, ShouldEqual, io.EOF)
})
})
})
})
}
}
func TestLimitReaderDecoder(t *testing.T) {
Convey("Test decoder with limit reader", t, func() {
buf := bytes.NewBufferString("\x34\xe6\xb5\x8b\xe8\xaf\x95123")
reader := newLimitReader(buf, 7)
decoder, err := NewDecoder(reader)
So(err, ShouldBeNil)
So(decoder.Type(), ShouldEqual, MESSAGE)
err = decoder.Close()
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, "123")
})
}

View File

@ -1,212 +0,0 @@
package parser
import (
"bytes"
"io"
"runtime"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestStringPayload(t *testing.T) {
type packet struct {
Type PacketType
Data []byte
IsString bool
}
type Test struct {
name string
packets []packet
output string
}
var tests = []Test{
{"all in one", []packet{packet{OPEN, nil, true}, packet{MESSAGE, []byte("测试"), true}, packet{MESSAGE, []byte("测试"), false}}, "\x31\x3a\x30\x37\x3a\x34\xe6\xb5\x8b\xe8\xaf\x95\x31\x30\x3a\x62\x34\x35\x72\x57\x4c\x36\x4b\x2b\x56"},
}
for _, test := range tests {
buf := bytes.NewBuffer(nil)
Convey("Given an array of packet "+test.name, t, func() {
Convey("Create encoder", func() {
encoder := NewStringPayloadEncoder()
So(encoder.IsString(), ShouldBeTrue)
Convey("Encoded", func() {
for _, p := range test.packets {
var e io.WriteCloser
var err error
if p.IsString {
e, err = encoder.NextString(p.Type)
} else {
e, err = encoder.NextBinary(p.Type)
}
So(err, ShouldBeNil)
for d := p.Data; len(d) > 0; {
n, err := e.Write(d)
So(err, ShouldBeNil)
d = d[n:]
}
err = e.Close()
So(err, ShouldBeNil)
}
Convey("End", func() {
err := encoder.EncodeTo(buf)
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, test.output)
})
})
})
Convey("Create decoder", func() {
decoder := NewPayloadDecoder(buf)
Convey("Decode", func() {
for i := 0; ; i++ {
d, err := decoder.Next()
if err == io.EOF {
break
}
So(err, ShouldBeNil)
So(d.Type(), ShouldEqual, test.packets[i].Type)
if l := len(test.packets[i].Data); l > 0 {
buf := make([]byte, len(test.packets[i].Data)+1)
n, err := d.Read(buf)
if n > 0 {
So(err, ShouldBeNil)
So(buf[:n], ShouldResemble, test.packets[i].Data)
}
_, err = d.Read(buf)
So(err, ShouldEqual, io.EOF)
}
err = d.Close()
So(err, ShouldBeNil)
}
})
})
})
}
}
func TestBinaryPayload(t *testing.T) {
type packet struct {
Type PacketType
Data []byte
IsString bool
}
type Test struct {
name string
packets []packet
output string
}
var tests = []Test{
{"all in one", []packet{packet{OPEN, nil, true}, packet{MESSAGE, []byte("测试"), true}, packet{MESSAGE, []byte("测试"), false}}, "\x00\x01\xff\x30\x00\x07\xff\x34\xe6\xb5\x8b\xe8\xaf\x95\x01\x07\xff\x04\xe6\xb5\x8b\xe8\xaf\x95"},
}
for _, test := range tests {
buf := bytes.NewBuffer(nil)
Convey("Given an array of packet "+test.name, t, func() {
Convey("Create encoder", func() {
encoder := NewBinaryPayloadEncoder()
So(encoder.IsString(), ShouldBeFalse)
Convey("Encoded", func() {
for _, p := range test.packets {
var e io.WriteCloser
var err error
if p.IsString {
e, err = encoder.NextString(p.Type)
} else {
e, err = encoder.NextBinary(p.Type)
}
So(err, ShouldBeNil)
for d := p.Data; len(d) > 0; {
n, err := e.Write(d)
So(err, ShouldBeNil)
d = d[n:]
}
err = e.Close()
So(err, ShouldBeNil)
}
Convey("End", func() {
err := encoder.EncodeTo(buf)
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, test.output)
})
})
})
Convey("Create decoder", func() {
decoder := NewPayloadDecoder(buf)
Convey("Decode", func() {
for i := 0; ; i++ {
d, err := decoder.Next()
if err == io.EOF {
break
}
So(err, ShouldBeNil)
So(d.Type(), ShouldEqual, test.packets[i].Type)
if l := len(test.packets[i].Data); l > 0 {
buf := make([]byte, len(test.packets[i].Data)+1)
n, err := d.Read(buf)
if n > 0 {
So(err, ShouldBeNil)
So(buf[:n], ShouldResemble, test.packets[i].Data)
}
_, err = d.Read(buf)
So(err, ShouldEqual, io.EOF)
}
err = d.Close()
So(err, ShouldBeNil)
}
})
})
})
}
}
func TestParallelEncode(t *testing.T) {
prev := runtime.GOMAXPROCS(10)
defer runtime.GOMAXPROCS(prev)
Convey("Test parallel encode", t, func() {
c := make(chan int)
max := 1000
buf1 := bytes.NewBuffer(nil)
buf2 := bytes.NewBuffer(nil)
encoder := NewStringPayloadEncoder()
for i := 0; i < max; i++ {
go func() {
e, _ := encoder.NextString(MESSAGE)
e.Write([]byte("1234"))
e.Close()
c <- 1
}()
}
for i := 0; i < max/2; i++ {
<-c
}
err := encoder.EncodeTo(buf1)
So(err, ShouldBeNil)
for i := 0; i < max/2; i++ {
<-c
}
err = encoder.EncodeTo(buf2)
So(err, ShouldBeNil)
for s := buf1.String(); len(s) > 0; {
So(s, ShouldStartWith, "5:41234")
s = s[len("5:41234"):]
}
for s := buf2.String(); len(s) > 0; {
So(s, ShouldStartWith, "5:41234")
s = s[len("5:41234"):]
}
})
}

View File

@ -1,231 +0,0 @@
package polling
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"github.com/googollee/go-engine.io/message"
"github.com/googollee/go-engine.io/parser"
"github.com/googollee/go-engine.io/transport"
. "github.com/smartystreets/goconvey/convey"
)
func TestPolling(t *testing.T) {
Convey("Normal", t, func() {
s := newServer()
server := httptest.NewServer(s)
defer server.Close()
req, err := http.NewRequest("GET", server.URL, nil)
So(err, ShouldBeNil)
client, err := NewClient(req)
So(err, ShouldBeNil)
So(client.Response(), ShouldBeNil)
sync := make(chan int)
go func() {
<-s.callback.onPacket
sync <- 1
}()
{
w, err := client.NextWriter(message.MessageBinary, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("123"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
}
{
<-sync
So(s.callback.messageType, ShouldEqual, message.MessageBinary)
So(s.callback.packetType, ShouldEqual, parser.MESSAGE)
So(s.callback.body, ShouldResemble, []byte("123"))
}
So(client.Response(), ShouldNotBeNil)
So(client.Response().StatusCode, ShouldEqual, http.StatusOK)
So(client.Response().Header.Get("Custom"), ShouldEqual, "value")
{
w, err := s.server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("abc1"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
w, err = s.server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("abc2"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
}
{
r, err := client.NextReader()
So(err, ShouldBeNil)
b, err := ioutil.ReadAll(r)
So(err, ShouldBeNil)
So(b, ShouldResemble, []byte("abc1"))
err = r.Close()
So(err, ShouldBeNil)
r, err = client.NextReader()
So(err, ShouldBeNil)
b, err = ioutil.ReadAll(r)
So(err, ShouldBeNil)
So(b, ShouldResemble, []byte("abc2"))
err = r.Close()
So(err, ShouldBeNil)
}
{
w, err := s.server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("abc"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
}
{
r, err := client.NextReader()
So(err, ShouldBeNil)
b, err := ioutil.ReadAll(r)
So(err, ShouldBeNil)
So(b, ShouldResemble, []byte("abc"))
err = r.Close()
So(err, ShouldBeNil)
}
client.Close()
})
Convey("Normal b64", t, func() {
s := newServer()
server := httptest.NewServer(s)
defer server.Close()
req, err := http.NewRequest("GET", server.URL+"?b64", nil)
So(err, ShouldBeNil)
client, err := NewClient(req)
So(err, ShouldBeNil)
So(client.Response(), ShouldBeNil)
sync := make(chan int)
go func() {
<-s.callback.onPacket
sync <- 1
}()
{
w, err := client.NextWriter(message.MessageBinary, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("123"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
}
{
<-sync
So(s.callback.messageType, ShouldEqual, message.MessageBinary)
So(s.callback.packetType, ShouldEqual, parser.MESSAGE)
So(s.callback.body, ShouldResemble, []byte("123"))
}
So(client.Response(), ShouldNotBeNil)
So(client.Response().StatusCode, ShouldEqual, http.StatusOK)
So(client.Response().Header.Get("Custom"), ShouldEqual, "value")
{
w, err := s.server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("abc1"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
w, err = s.server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("abc2"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
}
{
r, err := client.NextReader()
So(err, ShouldBeNil)
b, err := ioutil.ReadAll(r)
So(err, ShouldBeNil)
So(b, ShouldResemble, []byte("abc1"))
err = r.Close()
So(err, ShouldBeNil)
r, err = client.NextReader()
So(err, ShouldBeNil)
b, err = ioutil.ReadAll(r)
So(err, ShouldBeNil)
So(b, ShouldResemble, []byte("abc2"))
err = r.Close()
So(err, ShouldBeNil)
}
{
w, err := s.server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("abc"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
}
{
r, err := client.NextReader()
So(err, ShouldBeNil)
b, err := ioutil.ReadAll(r)
So(err, ShouldBeNil)
So(b, ShouldResemble, []byte("abc"))
err = r.Close()
So(err, ShouldBeNil)
}
client.Close()
})
}
type server struct {
server transport.Server
callback *fakeCallback
}
func newServer() *server {
return &server{
callback: newFakeCallback(),
}
}
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if s.server == nil {
var err error
s.server, err = NewServer(w, r, s.callback)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
w.Header().Set("Custom", "value")
s.server.ServeHTTP(w, r)
}

View File

@ -1,507 +0,0 @@
package polling
import (
"bytes"
"encoding/hex"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"sync"
"testing"
"time"
"github.com/googollee/go-engine.io/message"
"github.com/googollee/go-engine.io/parser"
"github.com/googollee/go-engine.io/transport"
. "github.com/smartystreets/goconvey/convey"
)
func TestServer(t *testing.T) {
Convey("Test polling", t, func() {
Convey("Overlay get", func() {
sync := make(chan int)
f := newFakeCallback()
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
go func() {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/", nil)
server.ServeHTTP(w, r)
sync <- 1
}()
time.Sleep(time.Second)
{
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusBadRequest)
So(w.Body.String(), ShouldEqual, "overlay get\n")
}
server.Close()
<-sync
So(f.ClosedCount(), ShouldEqual, 1)
})
Convey("Overlay post", func() {
sync := make(chan int)
f := newFakeCallback()
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
go func() {
w := httptest.NewRecorder()
r, _ := http.NewRequest("POST", "/", bytes.NewBufferString("\x00\x07\xff4测试"))
server.ServeHTTP(w, r)
sync <- 1
}()
time.Sleep(time.Second)
{
w := httptest.NewRecorder()
r, err := http.NewRequest("POST", "/", bytes.NewBufferString("\x00\x07\xff4测试"))
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusBadRequest)
So(w.Body.String(), ShouldEqual, "overlay post\n")
}
<-f.onPacket
server.Close()
<-sync
So(f.ClosedCount(), ShouldEqual, 1)
})
Convey("Get", func() {
f := newFakeCallback()
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
{
writer, err := server.NextWriter(message.MessageBinary, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = writer.Write([]byte("测试"))
So(err, ShouldBeNil)
err = writer.Close()
So(err, ShouldBeNil)
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusOK)
So(w.Header().Get("Content-Type"), ShouldEqual, "application/octet-stream")
So(hex.EncodeToString(w.Body.Bytes()), ShouldEqual, "0107ff04e6b58be8af95")
So(w.Body.String(), ShouldEqual, "\x01\x07\xff\x04测试")
}
{
writer, err := server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = writer.Write([]byte("测试"))
So(err, ShouldBeNil)
err = writer.Close()
So(err, ShouldBeNil)
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusOK)
So(w.Header().Get("Content-Type"), ShouldEqual, "application/octet-stream")
So(hex.EncodeToString(w.Body.Bytes()), ShouldEqual, "0007ff34e6b58be8af95")
So(w.Body.String(), ShouldEqual, "\x00\x07\xff4测试")
}
err = server.Close()
So(err, ShouldBeNil)
{
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusBadRequest)
So(w.Body.String(), ShouldEqual, "closed\n")
}
{
writer, err := server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldEqual, io.EOF)
So(writer, ShouldBeNil)
}
})
Convey("Get b64", func() {
f := newFakeCallback()
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/?b64", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
{
writer, err := server.NextWriter(message.MessageBinary, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = writer.Write([]byte("测试"))
So(err, ShouldBeNil)
err = writer.Close()
So(err, ShouldBeNil)
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusOK)
So(w.Header().Get("Content-Type"), ShouldEqual, "text/plain; charset=UTF-8")
So(w.Body.String(), ShouldEqual, "10:b45rWL6K+V")
}
{
writer, err := server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = writer.Write([]byte("测试"))
So(err, ShouldBeNil)
err = writer.Close()
So(err, ShouldBeNil)
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusOK)
So(w.Header().Get("Content-Type"), ShouldEqual, "text/plain; charset=UTF-8")
So(w.Body.String(), ShouldEqual, "7:4测试")
}
err = server.Close()
So(err, ShouldBeNil)
{
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusBadRequest)
So(w.Body.String(), ShouldEqual, "closed\n")
}
{
writer, err := server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldEqual, io.EOF)
So(writer, ShouldBeNil)
}
})
Convey("Post", func() {
f := newFakeCallback()
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
go func() {
<-f.onPacket
}()
{
w := httptest.NewRecorder()
r, err := http.NewRequest("POST", "/", bytes.NewBufferString("\x00\x07\xff4测试"))
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusOK)
So(w.Body.String(), ShouldEqual, "ok")
So(hex.EncodeToString(f.body), ShouldEqual, "e6b58be8af95")
}
{
w := httptest.NewRecorder()
r, err := http.NewRequest("POST", "/", bytes.NewBufferString("\x00\xff4测试"))
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusBadRequest)
So(w.Body.String(), ShouldEqual, "invalid input\n")
}
err = server.Close()
So(err, ShouldBeNil)
{
w := httptest.NewRecorder()
r, err := http.NewRequest("POST", "/", bytes.NewBufferString("\x00\x07\xff4测试"))
So(err, ShouldBeNil)
server.ServeHTTP(w, r)
So(w.Code, ShouldEqual, http.StatusBadRequest)
So(w.Body.String(), ShouldEqual, "closed\n")
}
})
Convey("Closing", func() {
Convey("No get no post", func() {
f := newFakeCallback()
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
So(f.ClosedCount(), ShouldEqual, 0)
err = server.Close()
So(err, ShouldBeNil)
So(f.ClosedCount(), ShouldEqual, 1)
So(f.closeServer, ShouldEqual, server)
})
Convey("No get has post", func() {
f := newFakeCallback()
sync := make(chan int)
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
go func() {
w := httptest.NewRecorder()
r, _ := http.NewRequest("POST", "/", bytes.NewBufferString("\x00\x07\xff4测试"))
server.ServeHTTP(w, r)
sync <- 1
}()
time.Sleep(time.Second)
So(f.ClosedCount(), ShouldEqual, 0)
err = server.Close()
So(err, ShouldBeNil)
So(f.ClosedCount(), ShouldEqual, 0)
<-f.onPacket
<-sync
So(f.closeServer, ShouldEqual, server)
So(f.ClosedCount(), ShouldEqual, 1)
})
Convey("has get no post", func() {
f := newFakeCallback()
sync := make(chan int)
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
go func() {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/", nil)
server.ServeHTTP(w, r)
sync <- 1
}()
time.Sleep(time.Second)
So(f.ClosedCount(), ShouldEqual, 0)
err = server.Close()
So(err, ShouldBeNil)
<-sync
So(f.closeServer, ShouldEqual, server)
So(f.ClosedCount(), ShouldEqual, 1)
})
Convey("has get has post", func() {
f := newFakeCallback()
sync := make(chan int)
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
go func() {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/", nil)
server.ServeHTTP(w, r)
sync <- 1
}()
go func() {
w := httptest.NewRecorder()
r, _ = http.NewRequest("POST", "/", bytes.NewBufferString("\x00\x07\xff4测试"))
server.ServeHTTP(w, r)
sync <- 1
}()
time.Sleep(time.Second)
So(f.ClosedCount(), ShouldEqual, 0)
err = server.Close()
So(err, ShouldBeNil)
So(f.ClosedCount(), ShouldEqual, 0)
<-f.onPacket
<-sync
<-sync
So(f.closeServer, ShouldEqual, server)
So(f.ClosedCount(), ShouldEqual, 1)
})
Convey("Multi-close", func() {
f := newFakeCallback()
sync := make(chan int)
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
go func() {
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/", nil)
server.ServeHTTP(w, r)
sync <- 1
}()
go func() {
w := httptest.NewRecorder()
r, _ := http.NewRequest("POST", "/", bytes.NewBufferString("\x00\x07\xff4测试"))
server.ServeHTTP(w, r)
sync <- 1
}()
time.Sleep(time.Second)
So(f.ClosedCount(), ShouldEqual, 0)
err = server.Close()
So(err, ShouldBeNil)
So(f.ClosedCount(), ShouldEqual, 0)
<-f.onPacket
<-sync
<-sync
So(f.closeServer, ShouldEqual, server)
server.Close()
server.Close()
server.Close()
server.Close()
So(f.ClosedCount(), ShouldEqual, 1)
})
Convey("Closed before writer closed", func() {
f := newFakeCallback()
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
server, err := NewServer(w, r, f)
So(err, ShouldBeNil)
writer, err := server.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
err = server.Close()
So(err, ShouldBeNil)
err = writer.Close()
So(err, ShouldNotBeNil)
})
})
})
}
type fakeCallback struct {
onPacket chan bool
messageType message.MessageType
packetType parser.PacketType
body []byte
err error
closedCount int
countLocker sync.Mutex
closeServer transport.Server
}
func newFakeCallback() *fakeCallback {
return &fakeCallback{
onPacket: make(chan bool),
}
}
func (f *fakeCallback) OnPacket(r *parser.PacketDecoder) {
f.packetType = r.Type()
f.messageType = r.MessageType()
f.body, f.err = ioutil.ReadAll(r)
f.onPacket <- true
}
func (f *fakeCallback) OnClose(s transport.Server) {
f.countLocker.Lock()
defer f.countLocker.Unlock()
f.closedCount++
f.closeServer = s
}
func (f *fakeCallback) ClosedCount() int {
f.countLocker.Lock()
defer f.countLocker.Unlock()
return f.closedCount
}

View File

@ -1,47 +0,0 @@
package polling
import (
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func TestTryLocker(t *testing.T) {
Convey("Lock/Unlock", t, func() {
locker := NewLocker()
sync := make(chan int)
go func() {
locker.Lock()
sync <- 1
time.Sleep(time.Second)
locker.Unlock()
}()
<-sync
start := time.Now()
locker.Lock()
now := time.Now()
So(now.Sub(start), ShouldBeGreaterThanOrEqualTo, time.Second)
locker.Unlock()
})
Convey("TryLock/Unlock", t, func() {
locker := NewLocker()
sync := make(chan int)
go func() {
locker.Lock()
sync <- 1
time.Sleep(time.Second)
locker.Unlock()
}()
<-sync
ok := locker.TryLock()
So(ok, ShouldBeFalse)
time.Sleep(time.Second * 3 / 2)
ok = locker.TryLock()
So(ok, ShouldBeTrue)
locker.Unlock()
})
}

View File

@ -1,89 +0,0 @@
package polling
import (
"bytes"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestWriter(t *testing.T) {
p := &Polling{
state: stateNormal,
sendChan: MakeSendChan(),
}
sendChan := p.sendChan
Convey("Wait close", t, func() {
w := newFakeWriteCloser()
select {
case <-sendChan:
panic("should not run here")
default:
}
writer := NewWriter(w, p)
err := writer.Close()
So(err, ShouldBeNil)
select {
case <-sendChan:
default:
panic("should not run here")
}
select {
case <-sendChan:
panic("should not run here")
default:
}
})
Convey("Many writer with close", t, func() {
for i := 0; i < 10; i++ {
w := newFakeWriteCloser()
writer := NewWriter(w, p)
err := writer.Close()
So(err, ShouldBeNil)
}
select {
case <-sendChan:
default:
panic("should not run here")
}
select {
case <-sendChan:
panic("should not run here")
default:
}
})
Convey("Close with not normal", t, func() {
p := &Polling{
state: stateClosing,
sendChan: MakeSendChan(),
}
w := newFakeWriteCloser()
writer := NewWriter(w, p)
err := writer.Close()
So(err, ShouldNotBeNil)
})
}
type fakeWriteCloser struct {
*bytes.Buffer
}
func newFakeWriteCloser() *fakeWriteCloser {
return &fakeWriteCloser{
Buffer: bytes.NewBuffer(nil),
}
}
func (f *fakeWriteCloser) Close() error {
return nil
}

View File

@ -1,377 +0,0 @@
package engineio
import (
"net/http"
"net/http/httptest"
"net/url"
"sync"
"testing"
"time"
"github.com/googollee/go-engine.io/message"
"github.com/googollee/go-engine.io/parser"
"github.com/googollee/go-engine.io/polling"
"github.com/googollee/go-engine.io/websocket"
. "github.com/smartystreets/goconvey/convey"
)
type FakeServer struct {
config *config
creaters transportCreaters
closed map[string]int
closedLocker sync.Mutex
}
func newFakeServer() *FakeServer {
return &FakeServer{
config: &config{
PingTimeout: time.Second * 2,
PingInterval: time.Second * 1,
AllowUpgrades: true,
},
creaters: transportCreaters{
"polling": polling.Creater,
"websocket": websocket.Creater,
},
closed: make(map[string]int),
}
}
func (f *FakeServer) configure() config {
return *f.config
}
func (f *FakeServer) transports() transportCreaters {
return f.creaters
}
func (f *FakeServer) onClose(sid string) {
f.closedLocker.Lock()
defer f.closedLocker.Unlock()
f.closed[sid] = f.closed[sid] + 1
}
func TestConn(t *testing.T) {
Convey("Create conn", t, func() {
Convey("without transport", func() {
server := newFakeServer()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
resp := httptest.NewRecorder()
_, err = newServerConn("id", resp, req, server)
So(err, ShouldEqual, InvalidError)
})
Convey("with invalid transport", func() {
server := newFakeServer()
req, err := http.NewRequest("GET", "/?transport=websocket", nil)
So(err, ShouldBeNil)
resp := httptest.NewRecorder()
_, err = newServerConn("id", resp, req, server)
So(err, ShouldNotBeNil)
})
Convey("ok", func() {
Convey("with polling", func() {
server := newFakeServer()
req, err := http.NewRequest("GET", "/?transport=polling", nil)
So(err, ShouldBeNil)
resp := httptest.NewRecorder()
conn, err := newServerConn("id", resp, req, server)
So(err, ShouldBeNil)
So(conn.Id(), ShouldEqual, "id")
So(conn.Request(), ShouldEqual, req)
conn.Close()
})
Convey("with websocket", func() {
server := newFakeServer()
h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
conn, err := newServerConn("id", w, r, server)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
if conn.Id() != "id" {
t.Fatal(err)
}
if conn.Request() != r {
t.Fatal(err)
}
}))
defer h.Close()
u, _ := url.Parse(h.URL)
u.Scheme = "ws"
req, err := http.NewRequest("GET", u.String()+"/?transport=websocket", nil)
So(err, ShouldBeNil)
So(req, ShouldNotBeNil)
c, err := websocket.NewClient(req)
So(err, ShouldBeNil)
defer c.Close()
})
})
})
Convey("Upgrade conn", t, func() {
Convey("polling to websocket", func() {
server := newFakeServer()
id := "id"
var conn *serverConn
h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if conn == nil {
var err error
conn, err = newServerConn(id, w, r, server)
if err != nil {
t.Fatal(err)
}
}
conn.ServeHTTP(w, r)
}))
defer h.Close()
u, err := url.Parse(h.URL)
So(err, ShouldBeNil)
req, err := http.NewRequest("GET", u.String()+"/?transport=polling", nil)
So(err, ShouldBeNil)
pc, err := polling.NewClient(req)
So(err, ShouldBeNil)
decoder, err := pc.NextReader()
So(err, ShouldBeNil)
So(pc.Response().StatusCode, ShouldEqual, http.StatusOK)
So(conn, ShouldNotBeNil)
So(conn, ShouldImplement, (*Conn)(nil))
So(decoder.MessageType(), ShouldEqual, message.MessageText)
So(decoder.Type(), ShouldEqual, parser.OPEN)
So(conn.getCurrent(), ShouldNotBeNil)
So(conn.getUpgrade(), ShouldBeNil)
u.Scheme = "ws"
req, err = http.NewRequest("GET", u.String()+"/?transport=websocket", nil)
So(err, ShouldBeNil)
wc, err := websocket.NewClient(req)
So(err, ShouldBeNil)
So(conn.getCurrent(), ShouldNotBeNil)
So(conn.getUpgrade(), ShouldNotBeNil)
encoder, err := wc.NextWriter(message.MessageBinary, parser.PING)
So(err, ShouldBeNil)
encoder.Write([]byte("probe"))
encoder.Close()
decoder, err = wc.NextReader()
So(err, ShouldBeNil)
So(wc.Response().StatusCode, ShouldEqual, http.StatusSwitchingProtocols)
So(decoder.MessageType(), ShouldEqual, message.MessageText)
So(decoder.Type(), ShouldEqual, parser.PONG)
pc.Close()
encoder, err = wc.NextWriter(message.MessageBinary, parser.UPGRADE)
So(err, ShouldBeNil)
encoder.Close()
decoder, err = wc.NextReader()
So(err, ShouldBeNil)
So(pc.Response().StatusCode, ShouldEqual, http.StatusOK)
So(decoder.MessageType(), ShouldEqual, message.MessageText)
So(decoder.Type(), ShouldEqual, parser.PING)
So(conn.getCurrent(), ShouldNotBeNil)
So(conn.getUpgrade(), ShouldBeNil)
wc.Close()
conn.Close()
time.Sleep(time.Second)
server.closedLocker.Lock()
So(server.closed[id], ShouldEqual, 1)
server.closedLocker.Unlock()
err = conn.Close()
So(err, ShouldBeNil)
time.Sleep(time.Second)
server.closedLocker.Lock()
So(server.closed[id], ShouldEqual, 1)
server.closedLocker.Unlock()
})
Convey("close when upgrading", func() {
server := newFakeServer()
id := "id"
var conn *serverConn
h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if conn == nil {
var err error
conn, err = newServerConn(id, w, r, server)
if err != nil {
t.Fatal(err)
}
}
conn.ServeHTTP(w, r)
}))
defer h.Close()
u, err := url.Parse(h.URL)
So(err, ShouldBeNil)
req, err := http.NewRequest("GET", u.String()+"/?transport=polling", nil)
So(err, ShouldBeNil)
pc, err := polling.NewClient(req)
So(err, ShouldBeNil)
decoder, err := pc.NextReader()
So(err, ShouldBeNil)
So(pc.Response().StatusCode, ShouldEqual, http.StatusOK)
So(conn, ShouldNotBeNil)
So(conn, ShouldImplement, (*Conn)(nil))
So(decoder.MessageType(), ShouldEqual, message.MessageText)
So(decoder.Type(), ShouldEqual, parser.OPEN)
So(conn.getCurrent(), ShouldNotBeNil)
So(conn.getUpgrade(), ShouldBeNil)
u.Scheme = "ws"
req, err = http.NewRequest("GET", u.String()+"/?transport=websocket", nil)
So(err, ShouldBeNil)
wc, err := websocket.NewClient(req)
So(err, ShouldBeNil)
So(conn.getCurrent(), ShouldNotBeNil)
So(conn.getUpgrade(), ShouldNotBeNil)
encoder, err := wc.NextWriter(message.MessageBinary, parser.PING)
So(err, ShouldBeNil)
encoder.Write([]byte("probe"))
encoder.Close()
decoder, err = wc.NextReader()
So(err, ShouldBeNil)
So(wc.Response().StatusCode, ShouldEqual, http.StatusSwitchingProtocols)
err = conn.Close()
So(err, ShouldBeNil)
wc.Close()
pc.Close()
time.Sleep(time.Second)
server.closedLocker.Lock()
So(server.closed[id], ShouldEqual, 1)
server.closedLocker.Unlock()
})
})
Convey("Closing", t, func() {
Convey("close timeout by polling", func() {
server := newFakeServer()
id := "id"
var conn *serverConn
h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if conn == nil {
var err error
conn, err = newServerConn(id, w, r, server)
if err != nil {
t.Fatal(err)
}
}
conn.ServeHTTP(w, r)
}))
defer h.Close()
u, err := url.Parse(h.URL)
So(err, ShouldBeNil)
req, err := http.NewRequest("GET", u.String()+"/?transport=polling", nil)
So(err, ShouldBeNil)
pc, err := polling.NewClient(req)
So(err, ShouldBeNil)
decoder, err := pc.NextReader()
So(err, ShouldBeNil)
So(decoder.MessageType(), ShouldEqual, message.MessageText)
So(decoder.Type(), ShouldEqual, parser.OPEN)
pc.Close()
time.Sleep(time.Second * 3)
server.closedLocker.Lock()
So(server.closed[id], ShouldEqual, 1)
server.closedLocker.Unlock()
err = conn.Close()
So(err, ShouldBeNil)
})
Convey("close by websocket", func() {
server := newFakeServer()
id := "id"
var conn *serverConn
var locker sync.Mutex
h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
locker.Lock()
defer locker.Unlock()
if conn == nil {
var err error
conn, err = newServerConn(id, w, r, server)
if err != nil {
t.Fatal(err)
}
}
conn.ServeHTTP(w, r)
}))
defer h.Close()
u, err := url.Parse(h.URL)
So(err, ShouldBeNil)
u.Scheme = "ws"
req, err := http.NewRequest("GET", u.String()+"/?transport=websocket", nil)
So(err, ShouldBeNil)
wc, err := websocket.NewClient(req)
So(err, ShouldBeNil)
wc.Close()
time.Sleep(time.Second / 2)
server.closedLocker.Lock()
So(server.closed[id], ShouldEqual, 1)
server.closedLocker.Unlock()
locker.Lock()
err = conn.Close()
locker.Unlock()
So(err, ShouldBeNil)
})
})
}

View File

@ -1,92 +0,0 @@
package engineio
import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/googollee/go-engine.io/parser"
. "github.com/smartystreets/goconvey/convey"
)
func TestServer(t *testing.T) {
Convey("Setup server", t, func() {
server, err := NewServer(nil)
So(err, ShouldBeNil)
server.SetPingInterval(time.Second)
So(server.config.PingInterval, ShouldEqual, time.Second)
server.SetPingTimeout(10 * time.Second)
So(server.config.PingTimeout, ShouldEqual, 10*time.Second)
f := func(*http.Request) error { return nil }
server.SetAllowRequest(f)
So(server.config.AllowRequest, ShouldEqual, f)
server.SetAllowUpgrades(false)
So(server.config.AllowUpgrades, ShouldBeFalse)
server.SetCookie("prefix")
So(server.config.Cookie, ShouldEqual, "prefix")
So(server.GetMaxConnection(), ShouldEqual, 1000)
})
Convey("Create server", t, func() {
Convey("Test new id", func() {
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
id1 := newId(req)
id2 := newId(req)
So(id1, ShouldNotEqual, id2)
})
})
Convey("Max connections", t, func() {
server, _ := NewServer(nil)
server.SetMaxConnection(1)
go func() {
for i := 0; i < 3; i++ {
server.Accept()
}
}()
req1 := newOpenReq()
res1 := httptest.NewRecorder()
server.ServeHTTP(res1, req1)
So(res1.Code, ShouldEqual, 200)
req2 := newOpenReq()
res2 := httptest.NewRecorder()
server.ServeHTTP(res2, req2)
So(res2.Code, ShouldEqual, 503)
So(strings.TrimSpace(string(res2.Body.Bytes())), ShouldEqual, "too many connections")
server.onClose(extractSid(res1.Body))
req3 := newOpenReq()
res3 := httptest.NewRecorder()
server.ServeHTTP(res3, req3)
So(res3.Code, ShouldEqual, 200)
})
}
func newOpenReq() *http.Request {
openReq, _ := http.NewRequest("GET", "/", bytes.NewBuffer([]byte{}))
q := openReq.URL.Query()
q.Set("transport", "polling")
openReq.URL.RawQuery = q.Encode()
return openReq
}
func extractSid(body io.Reader) string {
payload := parser.NewPayloadDecoder(body)
packet, _ := payload.Next()
openRes := map[string]interface{}{}
json.NewDecoder(packet).Decode(&openRes)
return openRes["sid"].(string)
}

View File

@ -1,25 +0,0 @@
package engineio
import (
. "github.com/smartystreets/goconvey/convey"
"testing"
)
func TestServerSessions(t *testing.T) {
Convey("Server sessions", t, func() {
sessions := newServerSessions()
So(sessions.Get("a"), ShouldBeNil)
sessions.Set("b", new(serverConn))
So(sessions.Get("b"), ShouldNotBeNil)
So(sessions.Get("a"), ShouldBeNil)
sessions.Set("c", new(serverConn))
So(sessions.Get("c"), ShouldNotBeNil)
sessions.Remove("b")
So(sessions.Get("b"), ShouldBeNil)
})
}

View File

@ -1,458 +0,0 @@
package websocket
import (
"encoding/hex"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"sync"
"testing"
"time"
"github.com/googollee/go-engine.io/transport"
"github.com/gorilla/websocket"
"github.com/googollee/go-engine.io/message"
"github.com/googollee/go-engine.io/parser"
. "github.com/smartystreets/goconvey/convey"
)
func TestWebsocket(t *testing.T) {
Convey("Creater", t, func() {
So(Creater.Name, ShouldEqual, "websocket")
So(Creater.Server, ShouldEqual, NewServer)
So(Creater.Client, ShouldEqual, NewClient)
})
Convey("Normal work, server part", t, func() {
sync := make(chan int)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
f := newFakeCallback()
s, err := NewServer(w, r, f)
if err != nil {
t.Fatal(err)
}
defer s.Close()
{
req, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}
recoder := httptest.NewRecorder()
s.ServeHTTP(recoder, req)
if recoder.Code != http.StatusBadRequest {
t.Fatal(recoder.Code, "!=", http.StatusBadRequest)
}
}
{
w, err := s.NextWriter(message.MessageText, parser.OPEN)
if err != nil {
t.Fatal(err)
}
err = w.Close()
if err != nil {
t.Fatal(err)
}
}
{
<-f.onPacket
if f.messageType != message.MessageBinary {
t.Fatal(f.messageType, "!=", message.MessageBinary)
}
if f.packetType != parser.MESSAGE {
t.Fatal(f.packetType, "!=", parser.MESSAGE)
}
if f.err != nil {
t.Fatal(err)
}
if body := string(f.body); body != "测试" {
t.Fatal(body, "!=", "测试")
}
}
<-sync
sync <- 1
<-sync
sync <- 1
{
w, err := s.NextWriter(message.MessageBinary, parser.NOOP)
if err != nil {
t.Fatal(err)
}
err = w.Close()
if err != nil {
t.Fatal(err)
}
}
<-sync
sync <- 1
{
<-f.onPacket
if f.messageType != message.MessageText {
t.Fatal(f.messageType, "!=", message.MessageText)
}
if f.packetType != parser.MESSAGE {
t.Fatal(f.packetType, "!=", parser.MESSAGE)
}
if f.err != nil {
t.Fatal(err)
}
if body := hex.EncodeToString(f.body); body != "e697a5e69cace8aa9e" {
t.Fatal(body, "!=", "e697a5e69cace8aa9e")
}
}
<-sync
sync <- 1
}))
defer server.Close()
u, _ := url.Parse(server.URL)
u.Scheme = "ws"
req, err := http.NewRequest("GET", u.String(), nil)
So(err, ShouldBeNil)
c, _ := NewClient(req)
defer c.Close()
{
w, _ := c.NextWriter(message.MessageBinary, parser.MESSAGE)
w.Write([]byte("测试"))
w.Close()
}
sync <- 1
<-sync
{
decoder, _ := c.NextReader()
defer decoder.Close()
ioutil.ReadAll(decoder)
}
sync <- 1
<-sync
{
decoder, _ := c.NextReader()
defer decoder.Close()
ioutil.ReadAll(decoder)
}
sync <- 1
<-sync
{
w, _ := c.NextWriter(message.MessageText, parser.MESSAGE)
w.Write([]byte("日本語"))
w.Close()
}
sync <- 1
<-sync
})
Convey("Normal work, client part", t, func() {
sync := make(chan int)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
f := newFakeCallback()
if v := r.URL.Query().Get("key"); v != "value" {
t.Fatal(v, "!=", "value")
}
s, _ := NewServer(w, r, f)
defer s.Close()
{
w, _ := s.NextWriter(message.MessageText, parser.OPEN)
w.Close()
}
{
<-f.onPacket
}
<-sync
sync <- 1
<-sync
sync <- 1
{
w, _ := s.NextWriter(message.MessageBinary, parser.NOOP)
w.Close()
}
<-sync
sync <- 1
{
<-f.onPacket
}
<-sync
sync <- 1
}))
defer server.Close()
u, err := url.Parse(server.URL)
So(err, ShouldBeNil)
u.Scheme = "ws"
req, err := http.NewRequest("GET", u.String()+"/?key=value", nil)
So(err, ShouldBeNil)
c, err := NewClient(req)
So(err, ShouldBeNil)
defer c.Close()
So(c.Response(), ShouldNotBeNil)
So(c.Response().StatusCode, ShouldEqual, http.StatusSwitchingProtocols)
{
w, err := c.NextWriter(message.MessageBinary, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("测试"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
}
sync <- 1
<-sync
{
decoder, err := c.NextReader()
So(err, ShouldBeNil)
defer decoder.Close()
So(decoder.MessageType(), ShouldEqual, message.MessageText)
So(decoder.Type(), ShouldEqual, parser.OPEN)
b, err := ioutil.ReadAll(decoder)
So(err, ShouldBeNil)
So(string(b), ShouldEqual, "")
}
sync <- 1
<-sync
{
decoder, err := c.NextReader()
So(err, ShouldBeNil)
defer decoder.Close()
So(decoder.MessageType(), ShouldEqual, message.MessageBinary)
So(decoder.Type(), ShouldEqual, parser.NOOP)
b, err := ioutil.ReadAll(decoder)
So(err, ShouldBeNil)
So(string(b), ShouldEqual, "")
}
sync <- 1
<-sync
{
w, err := c.NextWriter(message.MessageText, parser.MESSAGE)
So(err, ShouldBeNil)
_, err = w.Write([]byte("日本語"))
So(err, ShouldBeNil)
err = w.Close()
So(err, ShouldBeNil)
}
sync <- 1
<-sync
})
Convey("Packet content", t, func() {
sync := make(chan int)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
f := newFakeCallback()
s, _ := NewServer(w, r, f)
defer s.Close()
{
w, _ := s.NextWriter(message.MessageText, parser.MESSAGE)
w.Write([]byte("日本語"))
w.Close()
}
sync <- 1
<-sync
}))
defer server.Close()
u, err := url.Parse(server.URL)
So(err, ShouldBeNil)
u.Scheme = "ws"
req, err := http.NewRequest("GET", u.String(), nil)
So(err, ShouldBeNil)
c, err := NewClient(req)
So(err, ShouldBeNil)
defer c.Close()
{
client := c.(*client)
t, r, err := client.conn.NextReader()
So(err, ShouldBeNil)
So(t, ShouldEqual, websocket.TextMessage)
b, err := ioutil.ReadAll(r)
So(err, ShouldBeNil)
So(string(b), ShouldEqual, "4日本語")
So(hex.EncodeToString(b), ShouldEqual, "34e697a5e69cace8aa9e")
}
<-sync
sync <- 1
})
Convey("Close", t, func() {
f := newFakeCallback()
var s transport.Server
sync := make(chan int)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s, _ = NewServer(w, r, f)
s.Close()
s.Close()
s.Close()
sync <- 1
}))
defer server.Close()
u, err := url.Parse(server.URL)
So(err, ShouldBeNil)
u.Scheme = "ws"
req, err := http.NewRequest("GET", u.String(), nil)
So(err, ShouldBeNil)
c, err := NewClient(req)
So(err, ShouldBeNil)
defer c.Close()
<-sync
waitForClose(f)
So(f.ClosedCount(), ShouldEqual, 1)
So(f.closeServer, ShouldEqual, s)
})
Convey("Closing by disconnected", t, func() {
f := newFakeCallback()
sync := make(chan int)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s, _ := NewServer(w, r, f)
server := s.(*Server)
server.conn.Close()
sync <- 1
}))
defer server.Close()
u, err := url.Parse(server.URL)
So(err, ShouldBeNil)
u.Scheme = "ws"
req, err := http.NewRequest("GET", u.String(), nil)
So(err, ShouldBeNil)
c, err := NewClient(req)
So(err, ShouldBeNil)
defer c.Close()
<-sync
waitForClose(f)
So(f.ClosedCount(), ShouldEqual, 1)
})
Convey("Closing writer after closed", t, func() {
f := newFakeCallback()
sync := make(chan int)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s, err := NewServer(w, r, f)
if err != nil {
t.Fatal(err)
}
writer, err := s.NextWriter(message.MessageText, parser.MESSAGE)
if err != nil {
t.Fatal(err)
}
err = s.Close()
if err != nil {
t.Fatal(err)
}
err = writer.Close()
if err == nil {
t.Fatal("err should not be nil")
}
sync <- 1
}))
defer server.Close()
u, _ := url.Parse(server.URL)
u.Scheme = "ws"
req, _ := http.NewRequest("GET", u.String(), nil)
c, _ := NewClient(req)
defer c.Close()
<-sync
})
}
func waitForClose(f *fakeCallback) {
timeout := time.After(5 * time.Second)
var closed bool
select {
case <-f.closedChan:
closed = true
case <-timeout:
}
So(closed, ShouldBeTrue)
}
type fakeCallback struct {
onPacket chan bool
messageType message.MessageType
packetType parser.PacketType
body []byte
err error
closedCount int
countLocker sync.Mutex
closeServer transport.Server
closedChan chan struct{}
}
func newFakeCallback() *fakeCallback {
return &fakeCallback{
onPacket: make(chan bool),
closedChan: make(chan struct{}),
}
}
func (f *fakeCallback) OnPacket(r *parser.PacketDecoder) {
f.packetType = r.Type()
f.messageType = r.MessageType()
f.body, f.err = ioutil.ReadAll(r)
f.onPacket <- true
}
func (f *fakeCallback) OnClose(s transport.Server) {
f.countLocker.Lock()
defer f.countLocker.Unlock()
f.closedCount++
f.closeServer = s
if f.closedCount == 1 {
close(f.closedChan)
}
}
func (f *fakeCallback) ClosedCount() int {
f.countLocker.Lock()
defer f.countLocker.Unlock()
return f.closedCount
}

View File

@ -1,184 +0,0 @@
package socketio
import (
"bytes"
"encoding/json"
"io"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
type NoAttachment struct {
I int `json:"i"`
}
type HaveAttachment struct {
NoAttachment
A *Attachment `json:"a"`
}
func TestEncodeAttachments(t *testing.T) {
var input interface{}
var target []io.Reader
buf1 := bytes.NewBufferString("data1")
buf2 := bytes.NewBufferString("data2")
attachment1 := &Attachment{Data: buf1}
attachment2 := &Attachment{Data: buf2}
test := func() {
attachment1.num = -1
attachment2.num = -1
attachments := encodeAttachments(input)
if len(attachments)+len(target) > 0 {
So(attachments, ShouldResemble, target)
}
}
Convey("No attachment", t, func() {
input = &NoAttachment{}
target = nil
test()
})
Convey("Many attachment", t, func() {
input = &HaveAttachment{A: attachment1}
target = []io.Reader{buf1}
test()
So(attachment1.num, ShouldEqual, 0)
})
Convey("Array of attachments", t, func() {
input = [...]interface{}{HaveAttachment{A: attachment1}, &HaveAttachment{A: attachment2}}
target = []io.Reader{buf1, buf2}
test()
So(attachment1.num, ShouldEqual, 0)
So(attachment2.num, ShouldEqual, 1)
})
Convey("Slice of attachments", t, func() {
input = []interface{}{HaveAttachment{A: attachment1}, &HaveAttachment{A: attachment2}}
target = []io.Reader{buf1, buf2}
test()
So(attachment1.num, ShouldEqual, 0)
So(attachment2.num, ShouldEqual, 1)
})
Convey("Map of attachments", t, func() {
input = map[string]interface{}{"test": HaveAttachment{A: attachment1}, "testp": &HaveAttachment{A: attachment2}}
attachment1.num = -1
attachment2.num = -1
attachments := encodeAttachments(input)
So(attachment1.num, ShouldBeIn, []int{0, 1})
switch attachment1.num {
case 0:
So(attachment2.num, ShouldEqual, 1)
target = []io.Reader{buf1, buf2}
So(attachments, ShouldResemble, target)
case 1:
So(attachment2.num, ShouldEqual, 0)
target = []io.Reader{buf2, buf1}
So(attachments, ShouldResemble, target)
}
})
Convey("Encode attachment", t, func() {
input = map[string]interface{}{"test": HaveAttachment{A: attachment1}}
attachment1.num = -1
encodeAttachments(input)
b, err := json.Marshal(input)
So(err, ShouldBeNil)
So(string(b), ShouldEqual, `{"test":{"i":0,"a":{"_placeholder":true,"num":0}}}`)
})
}
func TestDecodeAttachments(t *testing.T) {
var input [][]byte
var v interface{}
buf1 := bytes.NewBuffer(nil)
buf2 := bytes.NewBuffer(nil)
var attachment1 *Attachment
var attachment2 *Attachment
test := func() {
err := decodeAttachments(v, input)
So(err, ShouldBeNil)
if attachment1 != nil {
So(buf1.String(), ShouldEqual, "data1")
}
if attachment2 != nil {
So(buf2.String(), ShouldEqual, "data2")
}
buf1.Reset()
buf2.Reset()
}
Convey("No attachment", t, func() {
input = nil
v = NoAttachment{}
test()
})
Convey("Many attachment", t, func() {
input = [][]byte{[]byte("data1")}
attachment1 = &Attachment{Data: buf1}
attachment1.num = 0
v = HaveAttachment{A: attachment1}
test()
})
Convey("Array of attachments", t, func() {
input = [][]byte{[]byte("data1"), []byte("data2")}
attachment1 = &Attachment{Data: buf1}
attachment1.num = 0
attachment2 = &Attachment{Data: buf2}
attachment2.num = 1
v = [...]interface{}{HaveAttachment{A: attachment1}, &HaveAttachment{A: attachment2}}
test()
})
Convey("Slice of attachments", t, func() {
input = [][]byte{[]byte("data1"), []byte("data2")}
attachment1 = &Attachment{Data: buf1}
attachment1.num = 0
attachment2 = &Attachment{Data: buf2}
attachment2.num = 1
v = []interface{}{HaveAttachment{A: attachment1}, &HaveAttachment{A: attachment2}}
test()
})
Convey("Map of attachments", t, func() {
input = [][]byte{[]byte("data1"), []byte("data2")}
attachment1 = &Attachment{Data: buf1}
attachment1.num = 0
attachment2 = &Attachment{Data: buf2}
attachment2.num = 1
v = map[string]interface{}{"test": HaveAttachment{A: attachment1}, "testp": &HaveAttachment{A: attachment2}}
test()
})
Convey("Deocde json", t, func() {
b := []byte(`{"i":0,"a":{"_placeholder":true,"num":2}}`)
v := &HaveAttachment{}
err := json.Unmarshal(b, &v)
So(err, ShouldBeNil)
So(v.A.num, ShouldEqual, 2)
})
}

View File

@ -1,38 +0,0 @@
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="/socket.io-1.3.7.js"></script>
<script src="/jquery-1.11.1.js"></script>
<script>
var socket = io();
$('form').submit(function(){
socket.emit('chat message with ack', $('#m').val(), function(data){
$('#messages').append($('<li>').text('ACK CALLBACK: ' + data));
});
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,53 +0,0 @@
package main
import (
"fmt"
"log"
"net/http"
"github.com/googollee/go-socket.io"
)
func main() {
server, err := socketio.NewServer(nil)
if err != nil {
log.Fatal(err)
}
server.On("connection", func(so socketio.Socket) {
log.Println("on connection")
so.Join("chat")
so.On("chat message", func(msg string) {
m := make(map[string]interface{})
m["a"] = "你好"
e := so.Emit("cn1111", m)
//这个没有问题
fmt.Println("\n\n")
b := make(map[string]string)
b["u-a"] = "中文内容" //这个不能是中文
m["b-c"] = b
e = so.Emit("cn2222", m)
log.Println(e)
log.Println("emit:", so.Emit("chat message", msg))
so.BroadcastTo("chat", "chat message", msg)
})
// Socket.io acknowledgement example
// The return type may vary depending on whether you will return
// For this example it is "string" type
so.On("chat message with ack", func(msg string) string {
return msg
})
so.On("disconnection", func() {
log.Println("on disconnect")
})
})
server.On("error", func(so socketio.Socket, err error) {
log.Println("error:", err)
})
http.Handle("/socket.io/", server)
http.Handle("/", http.FileServer(http.Dir("./asset")))
log.Println("Serving at localhost:5000...")
log.Fatal(http.ListenAndServe(":5000", nil))
}

View File

@ -1,105 +0,0 @@
package socketio
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
"net/http"
"io"
"github.com/googollee/go-engine.io"
)
type FakeBroadcastAdaptor struct {}
func (f *FakeBroadcastAdaptor) Join(room string, socket Socket) error {
return nil
}
func (f *FakeBroadcastAdaptor) Leave(room string, socket Socket) error {
return nil
}
func (f *FakeBroadcastAdaptor) Send(ignore Socket, room, event string, args ...interface{}) error {
return nil
}
type FakeReadCloser struct {}
func (fr *FakeReadCloser) Read(p []byte) (n int, err error) {
p = append(p, byte(128))
return 1, nil
}
func (fr *FakeReadCloser) Close() error {
return nil
}
type FakeWriteCloser struct {}
func (fr *FakeWriteCloser) Write(p []byte) (n int, err error) {
return len(p), nil
}
func (fr *FakeWriteCloser) Close() error {
return nil
}
type FakeSockConnection struct {}
func (f *FakeSockConnection) Id() string {
return "test1"
}
func (f *FakeSockConnection) Request() *http.Request {
return &http.Request{}
}
func (f *FakeSockConnection) Close() error {
return nil
}
func (f *FakeSockConnection) NextReader() (engineio.MessageType, io.ReadCloser, error) {
return engineio.MessageText, &FakeReadCloser{}, nil
}
func (f *FakeSockConnection) NextWriter(messageType engineio.MessageType) (io.WriteCloser, error) {
return &FakeWriteCloser{}, nil
}
func TestHandler(t *testing.T) {
//BugFix missed
//Method: handler.onPacket
//Reason: missed fallthrough after case _ACK:
//
// case _ACK:
// fallthrough <---- fixed problem
//
Convey("Call ACK handler by ACK id received from client", t, func() {
saver := &FrameSaver{}
var handlerCalled bool
baseHandlerInstance := newBaseHandler("some:event", &FakeBroadcastAdaptor{})
socketInstance := newSocket(&FakeSockConnection{}, baseHandlerInstance)
c, _ := newCaller(func () {handlerCalled = true})
socketInstance.acks[0] = c
socketInstance.onPacket(newDecoder(saver), &packet{Type:_ACK, Id:0, Data:"[]", NSP:"/"})
So(len(socketInstance.acks), ShouldEqual, 0)
So(handlerCalled, ShouldBeTrue)
})
Convey("Call BINARY ACK handler by BINARY ACK id received from client", t, func() {
saver := &FrameSaver{}
var handlerCalled bool
baseHandlerInstance := newBaseHandler("some:event", &FakeBroadcastAdaptor{})
socketInstance := newSocket(&FakeSockConnection{}, baseHandlerInstance)
c, _ := newCaller(func () {handlerCalled = true})
socketInstance.acks[0] = c
socketInstance.onPacket(newDecoder(saver), &packet{Type:_BINARY_ACK, Id:0, Data:"[]", NSP:"/"})
So(len(socketInstance.acks), ShouldEqual, 0)
So(handlerCalled, ShouldBeTrue)
})
}

View File

@ -1,50 +0,0 @@
package socketio
import (
"bytes"
"io"
"io/ioutil"
"github.com/googollee/go-engine.io"
)
type WriterNopCloser struct {
io.Writer
}
func NewWriterNopCloser(w io.Writer) io.WriteCloser {
return WriterNopCloser{
Writer: w,
}
}
func (w WriterNopCloser) Close() error {
return nil
}
type FrameData struct {
Buffer *bytes.Buffer
Type engineio.MessageType
}
type FrameSaver struct {
data []FrameData
}
func (f *FrameSaver) NextWriter(t engineio.MessageType) (io.WriteCloser, error) {
data := FrameData{
Buffer: bytes.NewBuffer(nil),
Type: t,
}
f.data = append(f.data, data)
return NewWriterNopCloser(data.Buffer), nil
}
func (f *FrameSaver) NextReader() (engineio.MessageType, io.ReadCloser, error) {
if len(f.data) == 0 {
return engineio.MessageText, nil, io.EOF
}
ret := f.data[0]
f.data = f.data[1:]
return ret.Type, ioutil.NopCloser(ret.Buffer), nil
}

View File

@ -1,54 +0,0 @@
package socketio
import (
"bufio"
"bytes"
"io/ioutil"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestMessageReader(t *testing.T) {
Convey("Read with args", t, func() {
buf := bufio.NewReader(bytes.NewBufferString(`["message",1]`))
reader, err := newMessageReader(buf)
So(err, ShouldBeNil)
So(reader.Message(), ShouldEqual, "message")
b, err := ioutil.ReadAll(reader)
So(err, ShouldBeNil)
So(string(b), ShouldEqual, "[1]")
})
Convey("Read with args, space", t, func() {
buf := bufio.NewReader(bytes.NewBufferString(`["message" , 1]`))
reader, err := newMessageReader(buf)
So(err, ShouldBeNil)
So(reader.Message(), ShouldEqual, "message")
b, err := ioutil.ReadAll(reader)
So(err, ShouldBeNil)
So(string(b), ShouldEqual, "[ 1]")
})
Convey("Read only message", t, func() {
buf := bufio.NewReader(bytes.NewBufferString(`["message"]`))
reader, err := newMessageReader(buf)
So(err, ShouldBeNil)
So(reader.Message(), ShouldEqual, "message")
b, err := ioutil.ReadAll(reader)
So(err, ShouldBeNil)
So(string(b), ShouldEqual, "[]")
})
Convey("Read only message", t, func() {
buf := bufio.NewReader(bytes.NewBufferString(`["message" ]`))
reader, err := newMessageReader(buf)
So(err, ShouldBeNil)
So(reader.Message(), ShouldEqual, "message")
b, err := ioutil.ReadAll(reader)
So(err, ShouldBeNil)
So(string(b), ShouldEqual, "[]")
})
}

View File

@ -1,191 +0,0 @@
package socketio
import (
"bytes"
"github.com/googollee/go-engine.io"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestPacketType(t *testing.T) {
Convey("Type string", t, func() {
So(_CONNECT, ShouldEqual, 0)
So(_CONNECT.String(), ShouldEqual, "connect")
So(_DISCONNECT, ShouldEqual, 1)
So(_DISCONNECT.String(), ShouldEqual, "disconnect")
So(_EVENT, ShouldEqual, 2)
So(_EVENT.String(), ShouldEqual, "event")
So(_ACK, ShouldEqual, 3)
So(_ACK.String(), ShouldEqual, "ack")
So(_ERROR, ShouldEqual, 4)
So(_ERROR.String(), ShouldEqual, "error")
So(_BINARY_EVENT, ShouldEqual, 5)
So(_BINARY_EVENT.String(), ShouldEqual, "binary_event")
So(_BINARY_ACK, ShouldEqual, 6)
So(_BINARY_ACK.String(), ShouldEqual, "binary_ack")
})
}
func TestParser(t *testing.T) {
p := packet{}
var decodeData interface{}
output := ""
message := ""
test := func() {
saver := &FrameSaver{}
encoder := newEncoder(saver)
err := encoder.Encode(p)
So(err, ShouldBeNil)
So(len(saver.data), ShouldBeGreaterThan, 0)
So(saver.data[0].Buffer.String(), ShouldEqual, output)
So(saver.data[0].Type, ShouldEqual, engineio.MessageText)
if len(saver.data) > 1 {
So(saver.data[1].Buffer.String(), ShouldEqual, "data")
So(saver.data[1].Type, ShouldEqual, engineio.MessageBinary)
}
d := packet{Data: decodeData}
decoder := newDecoder(saver)
err = decoder.Decode(&d)
So(err, ShouldBeNil)
So(d.Id, ShouldEqual, p.Id)
So(d.NSP, ShouldEqual, p.NSP)
if decodeData == nil {
So(d.Data, ShouldBeNil)
}
So(decoder.Message(), ShouldEqual, message)
err = decoder.DecodeData(&d)
So(err, ShouldBeNil)
So(d.Type, ShouldEqual, p.Type)
So(decoder.current, ShouldBeNil)
}
Convey("Only type", t, func() {
p = packet{
Type: _CONNECT,
Id: -1,
}
decodeData = nil
output = "0"
message = ""
test()
})
Convey("Type and id", t, func() {
p = packet{
Type: _EVENT,
Id: 1,
}
decodeData = nil
output = "21"
message = ""
test()
})
Convey("Type and namespace", t, func() {
p = packet{
Type: _EVENT,
Id: -1,
NSP: "/abc",
}
decodeData = nil
output = "2/abc"
message = ""
test()
})
Convey("Type, id and namespace", t, func() {
p = packet{
Type: _EVENT,
Id: 1,
NSP: "/abc",
}
decodeData = nil
output = "2/abc,1"
message = ""
test()
})
Convey("Type, namespace and data", t, func() {
p = packet{
Type: _EVENT,
Id: -1,
NSP: "/abc",
Data: []interface{}{"numbers", 1, 2, 3},
}
var i1, i2, i3 int
decodeData = &[]interface{}{&i1, &i2, &i3}
output = "2/abc,[\"numbers\",1,2,3]"
message = "numbers"
test()
So(i1, ShouldEqual, 1)
So(i2, ShouldEqual, 2)
So(i3, ShouldEqual, 3)
})
Convey("Type, namespace, id and data", t, func() {
p = packet{
Type: _EVENT,
Id: 1,
NSP: "/abc",
Data: []interface{}{"numbers", 1, 2, 3},
}
var i1, i2, i3 int
decodeData = &[]interface{}{&i1, &i2, &i3}
output = "2/abc,1[\"numbers\",1,2,3]"
message = "numbers"
test()
So(i1, ShouldEqual, 1)
So(i2, ShouldEqual, 2)
So(i3, ShouldEqual, 3)
})
Convey("Type, namespace, id and data(ack)", t, func() {
p = packet{
Type: _ACK,
Id: 1,
NSP: "/abc",
Data: []interface{}{1, 2, 3},
}
var i1, i2, i3 int
decodeData = &[]interface{}{&i1, &i2, &i3}
output = "3/abc,1[1,2,3]"
message = ""
test()
So(i1, ShouldEqual, 1)
So(i2, ShouldEqual, 2)
So(i3, ShouldEqual, 3)
})
Convey("Binary type with attachment", t, func() {
p = packet{
Type: _EVENT,
Id: 1,
NSP: "/abc",
Data: []interface{}{"binary", &Attachment{Data: bytes.NewBufferString("data")}},
}
buf := bytes.NewBuffer(nil)
decodeData = &[]interface{}{&Attachment{Data: buf}}
output = `51-/abc,1["binary",{"_placeholder":true,"num":0}]`
message = "binary"
test()
So(buf.String(), ShouldEqual, "data")
})
}

View File

@ -1,64 +0,0 @@
package socketio
import (
"bytes"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestTrimWriter(t *testing.T) {
var inputs []string
var target string
var trim string
test := func() {
buf := bytes.NewBuffer(nil)
w := newTrimWriter(buf, trim)
for _, str := range inputs {
_, err := w.Write([]byte(str))
So(err, ShouldBeNil)
}
So(buf.String(), ShouldEqual, target)
}
Convey("Trim nothing", t, func() {
inputs = []string{"1234", "2234"}
target = "12342234"
trim = ""
test()
})
Convey("Trim something", t, func() {
trim = "34"
Convey("Something at the end of final packet", func() {
inputs = []string{"1234", "2234"}
target = "123422"
test()
})
Convey("Something at the multiple packets", func() {
inputs = []string{"1234", "3434"}
target = "12"
test()
})
Convey("Something in the middle of packets", func() {
inputs = []string{"1234", "3434", "5678"}
target = "123434345678"
test()
})
Convey("Something in the middle and end of packets", func() {
inputs = []string{"1234", "3434", "5678", "9034"}
target = "12343434567890"
test()
})
})
}

View File

@ -1,512 +0,0 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"net/url"
"reflect"
"strings"
"testing"
"time"
)
var cstUpgrader = Upgrader{
Subprotocols: []string{"p0", "p1"},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
EnableCompression: true,
Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
http.Error(w, reason.Error(), status)
},
}
var cstDialer = Dialer{
Subprotocols: []string{"p1", "p2"},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
type cstHandler struct{ *testing.T }
type cstServer struct {
*httptest.Server
URL string
}
const (
cstPath = "/a/b"
cstRawQuery = "x=y"
cstRequestURI = cstPath + "?" + cstRawQuery
)
func newServer(t *testing.T) *cstServer {
var s cstServer
s.Server = httptest.NewServer(cstHandler{t})
s.Server.URL += cstRequestURI
s.URL = makeWsProto(s.Server.URL)
return &s
}
func newTLSServer(t *testing.T) *cstServer {
var s cstServer
s.Server = httptest.NewTLSServer(cstHandler{t})
s.Server.URL += cstRequestURI
s.URL = makeWsProto(s.Server.URL)
return &s
}
func (t cstHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != cstPath {
t.Logf("path=%v, want %v", r.URL.Path, cstPath)
http.Error(w, "bad path", 400)
return
}
if r.URL.RawQuery != cstRawQuery {
t.Logf("query=%v, want %v", r.URL.RawQuery, cstRawQuery)
http.Error(w, "bad path", 400)
return
}
subprotos := Subprotocols(r)
if !reflect.DeepEqual(subprotos, cstDialer.Subprotocols) {
t.Logf("subprotols=%v, want %v", subprotos, cstDialer.Subprotocols)
http.Error(w, "bad protocol", 400)
return
}
ws, err := cstUpgrader.Upgrade(w, r, http.Header{"Set-Cookie": {"sessionID=1234"}})
if err != nil {
t.Logf("Upgrade: %v", err)
return
}
defer ws.Close()
if ws.Subprotocol() != "p1" {
t.Logf("Subprotocol() = %s, want p1", ws.Subprotocol())
ws.Close()
return
}
op, rd, err := ws.NextReader()
if err != nil {
t.Logf("NextReader: %v", err)
return
}
wr, err := ws.NextWriter(op)
if err != nil {
t.Logf("NextWriter: %v", err)
return
}
if _, err = io.Copy(wr, rd); err != nil {
t.Logf("NextWriter: %v", err)
return
}
if err := wr.Close(); err != nil {
t.Logf("Close: %v", err)
return
}
}
func makeWsProto(s string) string {
return "ws" + strings.TrimPrefix(s, "http")
}
func sendRecv(t *testing.T, ws *Conn) {
const message = "Hello World!"
if err := ws.SetWriteDeadline(time.Now().Add(time.Second)); err != nil {
t.Fatalf("SetWriteDeadline: %v", err)
}
if err := ws.WriteMessage(TextMessage, []byte(message)); err != nil {
t.Fatalf("WriteMessage: %v", err)
}
if err := ws.SetReadDeadline(time.Now().Add(time.Second)); err != nil {
t.Fatalf("SetReadDeadline: %v", err)
}
_, p, err := ws.ReadMessage()
if err != nil {
t.Fatalf("ReadMessage: %v", err)
}
if string(p) != message {
t.Fatalf("message=%s, want %s", p, message)
}
}
func TestProxyDial(t *testing.T) {
s := newServer(t)
defer s.Close()
surl, _ := url.Parse(s.URL)
cstDialer.Proxy = http.ProxyURL(surl)
connect := false
origHandler := s.Server.Config.Handler
// Capture the request Host header.
s.Server.Config.Handler = http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if r.Method == "CONNECT" {
connect = true
w.WriteHeader(200)
return
}
if !connect {
t.Log("connect not recieved")
http.Error(w, "connect not recieved", 405)
return
}
origHandler.ServeHTTP(w, r)
})
ws, _, err := cstDialer.Dial(s.URL, nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()
sendRecv(t, ws)
cstDialer.Proxy = http.ProxyFromEnvironment
}
func TestProxyAuthorizationDial(t *testing.T) {
s := newServer(t)
defer s.Close()
surl, _ := url.Parse(s.URL)
surl.User = url.UserPassword("username", "password")
cstDialer.Proxy = http.ProxyURL(surl)
connect := false
origHandler := s.Server.Config.Handler
// Capture the request Host header.
s.Server.Config.Handler = http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
proxyAuth := r.Header.Get("Proxy-Authorization")
expectedProxyAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte("username:password"))
if r.Method == "CONNECT" && proxyAuth == expectedProxyAuth {
connect = true
w.WriteHeader(200)
return
}
if !connect {
t.Log("connect with proxy authorization not recieved")
http.Error(w, "connect with proxy authorization not recieved", 405)
return
}
origHandler.ServeHTTP(w, r)
})
ws, _, err := cstDialer.Dial(s.URL, nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()
sendRecv(t, ws)
cstDialer.Proxy = http.ProxyFromEnvironment
}
func TestDial(t *testing.T) {
s := newServer(t)
defer s.Close()
ws, _, err := cstDialer.Dial(s.URL, nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()
sendRecv(t, ws)
}
func TestDialCookieJar(t *testing.T) {
s := newServer(t)
defer s.Close()
jar, _ := cookiejar.New(nil)
d := cstDialer
d.Jar = jar
u, _ := parseURL(s.URL)
switch u.Scheme {
case "ws":
u.Scheme = "http"
case "wss":
u.Scheme = "https"
}
cookies := []*http.Cookie{&http.Cookie{Name: "gorilla", Value: "ws", Path: "/"}}
d.Jar.SetCookies(u, cookies)
ws, _, err := d.Dial(s.URL, nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()
var gorilla string
var sessionID string
for _, c := range d.Jar.Cookies(u) {
if c.Name == "gorilla" {
gorilla = c.Value
}
if c.Name == "sessionID" {
sessionID = c.Value
}
}
if gorilla != "ws" {
t.Error("Cookie not present in jar.")
}
if sessionID != "1234" {
t.Error("Set-Cookie not received from the server.")
}
sendRecv(t, ws)
}
func TestDialTLS(t *testing.T) {
s := newTLSServer(t)
defer s.Close()
certs := x509.NewCertPool()
for _, c := range s.TLS.Certificates {
roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1])
if err != nil {
t.Fatalf("error parsing server's root cert: %v", err)
}
for _, root := range roots {
certs.AddCert(root)
}
}
d := cstDialer
d.TLSClientConfig = &tls.Config{RootCAs: certs}
ws, _, err := d.Dial(s.URL, nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()
sendRecv(t, ws)
}
func xTestDialTLSBadCert(t *testing.T) {
// This test is deactivated because of noisy logging from the net/http package.
s := newTLSServer(t)
defer s.Close()
ws, _, err := cstDialer.Dial(s.URL, nil)
if err == nil {
ws.Close()
t.Fatalf("Dial: nil")
}
}
func TestDialTLSNoVerify(t *testing.T) {
s := newTLSServer(t)
defer s.Close()
d := cstDialer
d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
ws, _, err := d.Dial(s.URL, nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()
sendRecv(t, ws)
}
func TestDialTimeout(t *testing.T) {
s := newServer(t)
defer s.Close()
d := cstDialer
d.HandshakeTimeout = -1
ws, _, err := d.Dial(s.URL, nil)
if err == nil {
ws.Close()
t.Fatalf("Dial: nil")
}
}
func TestDialBadScheme(t *testing.T) {
s := newServer(t)
defer s.Close()
ws, _, err := cstDialer.Dial(s.Server.URL, nil)
if err == nil {
ws.Close()
t.Fatalf("Dial: nil")
}
}
func TestDialBadOrigin(t *testing.T) {
s := newServer(t)
defer s.Close()
ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {"bad"}})
if err == nil {
ws.Close()
t.Fatalf("Dial: nil")
}
if resp == nil {
t.Fatalf("resp=nil, err=%v", err)
}
if resp.StatusCode != http.StatusForbidden {
t.Fatalf("status=%d, want %d", resp.StatusCode, http.StatusForbidden)
}
}
func TestDialBadHeader(t *testing.T) {
s := newServer(t)
defer s.Close()
for _, k := range []string{"Upgrade",
"Connection",
"Sec-Websocket-Key",
"Sec-Websocket-Version",
"Sec-Websocket-Protocol"} {
h := http.Header{}
h.Set(k, "bad")
ws, _, err := cstDialer.Dial(s.URL, http.Header{"Origin": {"bad"}})
if err == nil {
ws.Close()
t.Errorf("Dial with header %s returned nil", k)
}
}
}
func TestBadMethod(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ws, err := cstUpgrader.Upgrade(w, r, nil)
if err == nil {
t.Errorf("handshake succeeded, expect fail")
ws.Close()
}
}))
defer s.Close()
resp, err := http.PostForm(s.URL, url.Values{})
if err != nil {
t.Fatalf("PostForm returned error %v", err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusMethodNotAllowed {
t.Errorf("Status = %d, want %d", resp.StatusCode, http.StatusMethodNotAllowed)
}
}
func TestHandshake(t *testing.T) {
s := newServer(t)
defer s.Close()
ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {s.URL}})
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()
var sessionID string
for _, c := range resp.Cookies() {
if c.Name == "sessionID" {
sessionID = c.Value
}
}
if sessionID != "1234" {
t.Error("Set-Cookie not received from the server.")
}
if ws.Subprotocol() != "p1" {
t.Errorf("ws.Subprotocol() = %s, want p1", ws.Subprotocol())
}
sendRecv(t, ws)
}
func TestRespOnBadHandshake(t *testing.T) {
const expectedStatus = http.StatusGone
const expectedBody = "This is the response body."
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(expectedStatus)
io.WriteString(w, expectedBody)
}))
defer s.Close()
ws, resp, err := cstDialer.Dial(makeWsProto(s.URL), nil)
if err == nil {
ws.Close()
t.Fatalf("Dial: nil")
}
if resp == nil {
t.Fatalf("resp=nil, err=%v", err)
}
if resp.StatusCode != expectedStatus {
t.Errorf("resp.StatusCode=%d, want %d", resp.StatusCode, expectedStatus)
}
p, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("ReadFull(resp.Body) returned error %v", err)
}
if string(p) != expectedBody {
t.Errorf("resp.Body=%s, want %s", p, expectedBody)
}
}
// TestHostHeader confirms that the host header provided in the call to Dial is
// sent to the server.
func TestHostHeader(t *testing.T) {
s := newServer(t)
defer s.Close()
specifiedHost := make(chan string, 1)
origHandler := s.Server.Config.Handler
// Capture the request Host header.
s.Server.Config.Handler = http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
specifiedHost <- r.Host
origHandler.ServeHTTP(w, r)
})
ws, _, err := cstDialer.Dial(s.URL, http.Header{"Host": {"testhost"}})
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()
if gotHost := <-specifiedHost; gotHost != "testhost" {
t.Fatalf("gotHost = %q, want \"testhost\"", gotHost)
}
sendRecv(t, ws)
}
func TestDialCompression(t *testing.T) {
s := newServer(t)
defer s.Close()
dialer := cstDialer
dialer.EnableCompression = true
ws, _, err := dialer.Dial(s.URL, nil)
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer ws.Close()
sendRecv(t, ws)
}

View File

@ -1,72 +0,0 @@
// Copyright 2014 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"net/url"
"reflect"
"testing"
)
var parseURLTests = []struct {
s string
u *url.URL
rui string
}{
{"ws://example.com/", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"},
{"ws://example.com", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"},
{"ws://example.com:7777/", &url.URL{Scheme: "ws", Host: "example.com:7777", Opaque: "/"}, "/"},
{"wss://example.com/", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/"}, "/"},
{"wss://example.com/a/b", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b"}, "/a/b"},
{"ss://example.com/a/b", nil, ""},
{"ws://webmaster@example.com/", nil, ""},
{"wss://example.com/a/b?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b", RawQuery: "x=y"}, "/a/b?x=y"},
{"wss://example.com?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/", RawQuery: "x=y"}, "/?x=y"},
}
func TestParseURL(t *testing.T) {
for _, tt := range parseURLTests {
u, err := parseURL(tt.s)
if tt.u != nil && err != nil {
t.Errorf("parseURL(%q) returned error %v", tt.s, err)
continue
}
if tt.u == nil {
if err == nil {
t.Errorf("parseURL(%q) did not return error", tt.s)
}
continue
}
if !reflect.DeepEqual(u, tt.u) {
t.Errorf("parseURL(%q) = %v, want %v", tt.s, u, tt.u)
continue
}
if u.RequestURI() != tt.rui {
t.Errorf("parseURL(%q).RequestURI() = %v, want %v", tt.s, u.RequestURI(), tt.rui)
}
}
}
var hostPortNoPortTests = []struct {
u *url.URL
hostPort, hostNoPort string
}{
{&url.URL{Scheme: "ws", Host: "example.com"}, "example.com:80", "example.com"},
{&url.URL{Scheme: "wss", Host: "example.com"}, "example.com:443", "example.com"},
{&url.URL{Scheme: "ws", Host: "example.com:7777"}, "example.com:7777", "example.com"},
{&url.URL{Scheme: "wss", Host: "example.com:7777"}, "example.com:7777", "example.com"},
}
func TestHostPortNoPort(t *testing.T) {
for _, tt := range hostPortNoPortTests {
hostPort, hostNoPort := hostPortNoPort(tt.u)
if hostPort != tt.hostPort {
t.Errorf("hostPortNoPort(%v) returned hostPort %q, want %q", tt.u, hostPort, tt.hostPort)
}
if hostNoPort != tt.hostNoPort {
t.Errorf("hostPortNoPort(%v) returned hostNoPort %q, want %q", tt.u, hostNoPort, tt.hostNoPort)
}
}
}

View File

@ -1,80 +0,0 @@
package websocket
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"testing"
)
type nopCloser struct{ io.Writer }
func (nopCloser) Close() error { return nil }
func TestTruncWriter(t *testing.T) {
const data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijlkmnopqrstuvwxyz987654321"
for n := 1; n <= 10; n++ {
var b bytes.Buffer
w := &truncWriter{w: nopCloser{&b}}
p := []byte(data)
for len(p) > 0 {
m := len(p)
if m > n {
m = n
}
w.Write(p[:m])
p = p[m:]
}
if b.String() != data[:len(data)-len(w.p)] {
t.Errorf("%d: %q", n, b.String())
}
}
}
func textMessages(num int) [][]byte {
messages := make([][]byte, num)
for i := 0; i < num; i++ {
msg := fmt.Sprintf("planet: %d, country: %d, city: %d, street: %d", i, i, i, i)
messages[i] = []byte(msg)
}
return messages
}
func BenchmarkWriteNoCompression(b *testing.B) {
w := ioutil.Discard
c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024)
messages := textMessages(100)
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.WriteMessage(TextMessage, messages[i%len(messages)])
}
b.ReportAllocs()
}
func BenchmarkWriteWithCompression(b *testing.B) {
w := ioutil.Discard
c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024)
messages := textMessages(100)
c.enableWriteCompression = true
c.newCompressionWriter = compressNoContextTakeover
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.WriteMessage(TextMessage, messages[i%len(messages)])
}
b.ReportAllocs()
}
func TestValidCompressionLevel(t *testing.T) {
c := newConn(fakeNetConn{}, false, 1024, 1024)
for _, level := range []int{minCompressionLevel - 1, maxCompressionLevel + 1} {
if err := c.SetCompressionLevel(level); err == nil {
t.Errorf("no error for level %d", level)
}
}
for _, level := range []int{minCompressionLevel, maxCompressionLevel} {
if err := c.SetCompressionLevel(level); err != nil {
t.Errorf("error for level %d", level)
}
}
}

View File

@ -1,134 +0,0 @@
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.7
package websocket
import (
"io"
"io/ioutil"
"sync/atomic"
"testing"
)
// broadcastBench allows to run broadcast benchmarks.
// In every broadcast benchmark we create many connections, then send the same
// message into every connection and wait for all writes complete. This emulates
// an application where many connections listen to the same data - i.e. PUB/SUB
// scenarios with many subscribers in one channel.
type broadcastBench struct {
w io.Writer
message *broadcastMessage
closeCh chan struct{}
doneCh chan struct{}
count int32
conns []*broadcastConn
compression bool
usePrepared bool
}
type broadcastMessage struct {
payload []byte
prepared *PreparedMessage
}
type broadcastConn struct {
conn *Conn
msgCh chan *broadcastMessage
}
func newBroadcastConn(c *Conn) *broadcastConn {
return &broadcastConn{
conn: c,
msgCh: make(chan *broadcastMessage, 1),
}
}
func newBroadcastBench(usePrepared, compression bool) *broadcastBench {
bench := &broadcastBench{
w: ioutil.Discard,
doneCh: make(chan struct{}),
closeCh: make(chan struct{}),
usePrepared: usePrepared,
compression: compression,
}
msg := &broadcastMessage{
payload: textMessages(1)[0],
}
if usePrepared {
pm, _ := NewPreparedMessage(TextMessage, msg.payload)
msg.prepared = pm
}
bench.message = msg
bench.makeConns(10000)
return bench
}
func (b *broadcastBench) makeConns(numConns int) {
conns := make([]*broadcastConn, numConns)
for i := 0; i < numConns; i++ {
c := newConn(fakeNetConn{Reader: nil, Writer: b.w}, true, 1024, 1024)
if b.compression {
c.enableWriteCompression = true
c.newCompressionWriter = compressNoContextTakeover
}
conns[i] = newBroadcastConn(c)
go func(c *broadcastConn) {
for {
select {
case msg := <-c.msgCh:
if b.usePrepared {
c.conn.WritePreparedMessage(msg.prepared)
} else {
c.conn.WriteMessage(TextMessage, msg.payload)
}
val := atomic.AddInt32(&b.count, 1)
if val%int32(numConns) == 0 {
b.doneCh <- struct{}{}
}
case <-b.closeCh:
return
}
}
}(conns[i])
}
b.conns = conns
}
func (b *broadcastBench) close() {
close(b.closeCh)
}
func (b *broadcastBench) runOnce() {
for _, c := range b.conns {
c.msgCh <- b.message
}
<-b.doneCh
}
func BenchmarkBroadcast(b *testing.B) {
benchmarks := []struct {
name string
usePrepared bool
compression bool
}{
{"NoCompression", false, false},
{"WithCompression", false, true},
{"NoCompressionPrepared", true, false},
{"WithCompressionPrepared", true, true},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
bench := newBroadcastBench(bm.usePrepared, bm.compression)
defer bench.close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
bench.runOnce()
}
b.ReportAllocs()
})
}
}

View File

@ -1,497 +0,0 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"reflect"
"testing"
"testing/iotest"
"time"
)
var _ net.Error = errWriteTimeout
type fakeNetConn struct {
io.Reader
io.Writer
}
func (c fakeNetConn) Close() error { return nil }
func (c fakeNetConn) LocalAddr() net.Addr { return localAddr }
func (c fakeNetConn) RemoteAddr() net.Addr { return remoteAddr }
func (c fakeNetConn) SetDeadline(t time.Time) error { return nil }
func (c fakeNetConn) SetReadDeadline(t time.Time) error { return nil }
func (c fakeNetConn) SetWriteDeadline(t time.Time) error { return nil }
type fakeAddr int
var (
localAddr = fakeAddr(1)
remoteAddr = fakeAddr(2)
)
func (a fakeAddr) Network() string {
return "net"
}
func (a fakeAddr) String() string {
return "str"
}
func TestFraming(t *testing.T) {
frameSizes := []int{0, 1, 2, 124, 125, 126, 127, 128, 129, 65534, 65535, 65536, 65537}
var readChunkers = []struct {
name string
f func(io.Reader) io.Reader
}{
{"half", iotest.HalfReader},
{"one", iotest.OneByteReader},
{"asis", func(r io.Reader) io.Reader { return r }},
}
writeBuf := make([]byte, 65537)
for i := range writeBuf {
writeBuf[i] = byte(i)
}
var writers = []struct {
name string
f func(w io.Writer, n int) (int, error)
}{
{"iocopy", func(w io.Writer, n int) (int, error) {
nn, err := io.Copy(w, bytes.NewReader(writeBuf[:n]))
return int(nn), err
}},
{"write", func(w io.Writer, n int) (int, error) {
return w.Write(writeBuf[:n])
}},
{"string", func(w io.Writer, n int) (int, error) {
return io.WriteString(w, string(writeBuf[:n]))
}},
}
for _, compress := range []bool{false, true} {
for _, isServer := range []bool{true, false} {
for _, chunker := range readChunkers {
var connBuf bytes.Buffer
wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024)
rc := newConn(fakeNetConn{Reader: chunker.f(&connBuf), Writer: nil}, !isServer, 1024, 1024)
if compress {
wc.newCompressionWriter = compressNoContextTakeover
rc.newDecompressionReader = decompressNoContextTakeover
}
for _, n := range frameSizes {
for _, writer := range writers {
name := fmt.Sprintf("z:%v, s:%v, r:%s, n:%d w:%s", compress, isServer, chunker.name, n, writer.name)
w, err := wc.NextWriter(TextMessage)
if err != nil {
t.Errorf("%s: wc.NextWriter() returned %v", name, err)
continue
}
nn, err := writer.f(w, n)
if err != nil || nn != n {
t.Errorf("%s: w.Write(writeBuf[:n]) returned %d, %v", name, nn, err)
continue
}
err = w.Close()
if err != nil {
t.Errorf("%s: w.Close() returned %v", name, err)
continue
}
opCode, r, err := rc.NextReader()
if err != nil || opCode != TextMessage {
t.Errorf("%s: NextReader() returned %d, r, %v", name, opCode, err)
continue
}
rbuf, err := ioutil.ReadAll(r)
if err != nil {
t.Errorf("%s: ReadFull() returned rbuf, %v", name, err)
continue
}
if len(rbuf) != n {
t.Errorf("%s: len(rbuf) is %d, want %d", name, len(rbuf), n)
continue
}
for i, b := range rbuf {
if byte(i) != b {
t.Errorf("%s: bad byte at offset %d", name, i)
break
}
}
}
}
}
}
}
}
func TestControl(t *testing.T) {
const message = "this is a ping/pong messsage"
for _, isServer := range []bool{true, false} {
for _, isWriteControl := range []bool{true, false} {
name := fmt.Sprintf("s:%v, wc:%v", isServer, isWriteControl)
var connBuf bytes.Buffer
wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024)
rc := newConn(fakeNetConn{Reader: &connBuf, Writer: nil}, !isServer, 1024, 1024)
if isWriteControl {
wc.WriteControl(PongMessage, []byte(message), time.Now().Add(time.Second))
} else {
w, err := wc.NextWriter(PongMessage)
if err != nil {
t.Errorf("%s: wc.NextWriter() returned %v", name, err)
continue
}
if _, err := w.Write([]byte(message)); err != nil {
t.Errorf("%s: w.Write() returned %v", name, err)
continue
}
if err := w.Close(); err != nil {
t.Errorf("%s: w.Close() returned %v", name, err)
continue
}
var actualMessage string
rc.SetPongHandler(func(s string) error { actualMessage = s; return nil })
rc.NextReader()
if actualMessage != message {
t.Errorf("%s: pong=%q, want %q", name, actualMessage, message)
continue
}
}
}
}
}
func TestCloseFrameBeforeFinalMessageFrame(t *testing.T) {
const bufSize = 512
expectedErr := &CloseError{Code: CloseNormalClosure, Text: "hello"}
var b1, b2 bytes.Buffer
wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize)
rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024)
w, _ := wc.NextWriter(BinaryMessage)
w.Write(make([]byte, bufSize+bufSize/2))
wc.WriteControl(CloseMessage, FormatCloseMessage(expectedErr.Code, expectedErr.Text), time.Now().Add(10*time.Second))
w.Close()
op, r, err := rc.NextReader()
if op != BinaryMessage || err != nil {
t.Fatalf("NextReader() returned %d, %v", op, err)
}
_, err = io.Copy(ioutil.Discard, r)
if !reflect.DeepEqual(err, expectedErr) {
t.Fatalf("io.Copy() returned %v, want %v", err, expectedErr)
}
_, _, err = rc.NextReader()
if !reflect.DeepEqual(err, expectedErr) {
t.Fatalf("NextReader() returned %v, want %v", err, expectedErr)
}
}
func TestEOFWithinFrame(t *testing.T) {
const bufSize = 64
for n := 0; ; n++ {
var b bytes.Buffer
wc := newConn(fakeNetConn{Reader: nil, Writer: &b}, false, 1024, 1024)
rc := newConn(fakeNetConn{Reader: &b, Writer: nil}, true, 1024, 1024)
w, _ := wc.NextWriter(BinaryMessage)
w.Write(make([]byte, bufSize))
w.Close()
if n >= b.Len() {
break
}
b.Truncate(n)
op, r, err := rc.NextReader()
if err == errUnexpectedEOF {
continue
}
if op != BinaryMessage || err != nil {
t.Fatalf("%d: NextReader() returned %d, %v", n, op, err)
}
_, err = io.Copy(ioutil.Discard, r)
if err != errUnexpectedEOF {
t.Fatalf("%d: io.Copy() returned %v, want %v", n, err, errUnexpectedEOF)
}
_, _, err = rc.NextReader()
if err != errUnexpectedEOF {
t.Fatalf("%d: NextReader() returned %v, want %v", n, err, errUnexpectedEOF)
}
}
}
func TestEOFBeforeFinalFrame(t *testing.T) {
const bufSize = 512
var b1, b2 bytes.Buffer
wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize)
rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024)
w, _ := wc.NextWriter(BinaryMessage)
w.Write(make([]byte, bufSize+bufSize/2))
op, r, err := rc.NextReader()
if op != BinaryMessage || err != nil {
t.Fatalf("NextReader() returned %d, %v", op, err)
}
_, err = io.Copy(ioutil.Discard, r)
if err != errUnexpectedEOF {
t.Fatalf("io.Copy() returned %v, want %v", err, errUnexpectedEOF)
}
_, _, err = rc.NextReader()
if err != errUnexpectedEOF {
t.Fatalf("NextReader() returned %v, want %v", err, errUnexpectedEOF)
}
}
func TestWriteAfterMessageWriterClose(t *testing.T) {
wc := newConn(fakeNetConn{Reader: nil, Writer: &bytes.Buffer{}}, false, 1024, 1024)
w, _ := wc.NextWriter(BinaryMessage)
io.WriteString(w, "hello")
if err := w.Close(); err != nil {
t.Fatalf("unxpected error closing message writer, %v", err)
}
if _, err := io.WriteString(w, "world"); err == nil {
t.Fatalf("no error writing after close")
}
w, _ = wc.NextWriter(BinaryMessage)
io.WriteString(w, "hello")
// close w by getting next writer
_, err := wc.NextWriter(BinaryMessage)
if err != nil {
t.Fatalf("unexpected error getting next writer, %v", err)
}
if _, err := io.WriteString(w, "world"); err == nil {
t.Fatalf("no error writing after close")
}
}
func TestReadLimit(t *testing.T) {
const readLimit = 512
message := make([]byte, readLimit+1)
var b1, b2 bytes.Buffer
wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, readLimit-2)
rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024)
rc.SetReadLimit(readLimit)
// Send message at the limit with interleaved pong.
w, _ := wc.NextWriter(BinaryMessage)
w.Write(message[:readLimit-1])
wc.WriteControl(PongMessage, []byte("this is a pong"), time.Now().Add(10*time.Second))
w.Write(message[:1])
w.Close()
// Send message larger than the limit.
wc.WriteMessage(BinaryMessage, message[:readLimit+1])
op, _, err := rc.NextReader()
if op != BinaryMessage || err != nil {
t.Fatalf("1: NextReader() returned %d, %v", op, err)
}
op, r, err := rc.NextReader()
if op != BinaryMessage || err != nil {
t.Fatalf("2: NextReader() returned %d, %v", op, err)
}
_, err = io.Copy(ioutil.Discard, r)
if err != ErrReadLimit {
t.Fatalf("io.Copy() returned %v", err)
}
}
func TestAddrs(t *testing.T) {
c := newConn(&fakeNetConn{}, true, 1024, 1024)
if c.LocalAddr() != localAddr {
t.Errorf("LocalAddr = %v, want %v", c.LocalAddr(), localAddr)
}
if c.RemoteAddr() != remoteAddr {
t.Errorf("RemoteAddr = %v, want %v", c.RemoteAddr(), remoteAddr)
}
}
func TestUnderlyingConn(t *testing.T) {
var b1, b2 bytes.Buffer
fc := fakeNetConn{Reader: &b1, Writer: &b2}
c := newConn(fc, true, 1024, 1024)
ul := c.UnderlyingConn()
if ul != fc {
t.Fatalf("Underlying conn is not what it should be.")
}
}
func TestBufioReadBytes(t *testing.T) {
// Test calling bufio.ReadBytes for value longer than read buffer size.
m := make([]byte, 512)
m[len(m)-1] = '\n'
var b1, b2 bytes.Buffer
wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, len(m)+64, len(m)+64)
rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, len(m)-64, len(m)-64)
w, _ := wc.NextWriter(BinaryMessage)
w.Write(m)
w.Close()
op, r, err := rc.NextReader()
if op != BinaryMessage || err != nil {
t.Fatalf("NextReader() returned %d, %v", op, err)
}
br := bufio.NewReader(r)
p, err := br.ReadBytes('\n')
if err != nil {
t.Fatalf("ReadBytes() returned %v", err)
}
if len(p) != len(m) {
t.Fatalf("read returnd %d bytes, want %d bytes", len(p), len(m))
}
}
var closeErrorTests = []struct {
err error
codes []int
ok bool
}{
{&CloseError{Code: CloseNormalClosure}, []int{CloseNormalClosure}, true},
{&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived}, false},
{&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived, CloseNormalClosure}, true},
{errors.New("hello"), []int{CloseNormalClosure}, false},
}
func TestCloseError(t *testing.T) {
for _, tt := range closeErrorTests {
ok := IsCloseError(tt.err, tt.codes...)
if ok != tt.ok {
t.Errorf("IsCloseError(%#v, %#v) returned %v, want %v", tt.err, tt.codes, ok, tt.ok)
}
}
}
var unexpectedCloseErrorTests = []struct {
err error
codes []int
ok bool
}{
{&CloseError{Code: CloseNormalClosure}, []int{CloseNormalClosure}, false},
{&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived}, true},
{&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived, CloseNormalClosure}, false},
{errors.New("hello"), []int{CloseNormalClosure}, false},
}
func TestUnexpectedCloseErrors(t *testing.T) {
for _, tt := range unexpectedCloseErrorTests {
ok := IsUnexpectedCloseError(tt.err, tt.codes...)
if ok != tt.ok {
t.Errorf("IsUnexpectedCloseError(%#v, %#v) returned %v, want %v", tt.err, tt.codes, ok, tt.ok)
}
}
}
type blockingWriter struct {
c1, c2 chan struct{}
}
func (w blockingWriter) Write(p []byte) (int, error) {
// Allow main to continue
close(w.c1)
// Wait for panic in main
<-w.c2
return len(p), nil
}
func TestConcurrentWritePanic(t *testing.T) {
w := blockingWriter{make(chan struct{}), make(chan struct{})}
c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024)
go func() {
c.WriteMessage(TextMessage, []byte{})
}()
// wait for goroutine to block in write.
<-w.c1
defer func() {
close(w.c2)
if v := recover(); v != nil {
return
}
}()
c.WriteMessage(TextMessage, []byte{})
t.Fatal("should not get here")
}
type failingReader struct{}
func (r failingReader) Read(p []byte) (int, error) {
return 0, io.EOF
}
func TestFailedConnectionReadPanic(t *testing.T) {
c := newConn(fakeNetConn{Reader: failingReader{}, Writer: nil}, false, 1024, 1024)
defer func() {
if v := recover(); v != nil {
return
}
}()
for i := 0; i < 20000; i++ {
c.ReadMessage()
}
t.Fatal("should not get here")
}
func TestBufioReuse(t *testing.T) {
brw := bufio.NewReadWriter(bufio.NewReader(nil), bufio.NewWriter(nil))
c := newConnBRW(nil, false, 0, 0, brw)
if c.br != brw.Reader {
t.Error("connection did not reuse bufio.Reader")
}
var wh writeHook
brw.Writer.Reset(&wh)
brw.WriteByte(0)
brw.Flush()
if &c.writeBuf[0] != &wh.p[0] {
t.Error("connection did not reuse bufio.Writer")
}
brw = bufio.NewReadWriter(bufio.NewReaderSize(nil, 0), bufio.NewWriterSize(nil, 0))
c = newConnBRW(nil, false, 0, 0, brw)
if c.br == brw.Reader {
t.Error("connection used bufio.Reader with small size")
}
brw.Writer.Reset(&wh)
brw.WriteByte(0)
brw.Flush()
if &c.writeBuf[0] != &wh.p[0] {
t.Error("connection used bufio.Writer with small size")
}
}

View File

@ -1,46 +0,0 @@
// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket_test
import (
"log"
"net/http"
"testing"
"github.com/gorilla/websocket"
)
var (
c *websocket.Conn
req *http.Request
)
// The websocket.IsUnexpectedCloseError function is useful for identifying
// application and protocol errors.
//
// This server application works with a client application running in the
// browser. The client application does not explicitly close the websocket. The
// only expected close message from the client has the code
// websocket.CloseGoingAway. All other other close messages are likely the
// result of an application or protocol error and are logged to aid debugging.
func ExampleIsUnexpectedCloseError() {
for {
messageType, p, err := c.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
log.Printf("error: %v, user-agent: %v", err, req.Header.Get("User-Agent"))
}
return
}
processMesage(messageType, p)
}
}
func processMesage(mt int, p []byte) {}
// TestX prevents godoc from showing this entire file in the example. Remove
// this function when a second example is added.
func TestX(t *testing.T) {}

View File

@ -1,13 +0,0 @@
# Test Server
This package contains a server for the [Autobahn WebSockets Test Suite](http://autobahn.ws/testsuite).
To test the server, run
go run server.go
and start the client test driver
wstest -m fuzzingclient -s fuzzingclient.json
When the client completes, it writes a report to reports/clients/index.html.

View File

@ -1,15 +0,0 @@
{
"options": {"failByDrop": false},
"outdir": "./reports/clients",
"servers": [
{"agent": "ReadAllWriteMessage", "url": "ws://localhost:9000/m", "options": {"version": 18}},
{"agent": "ReadAllWritePreparedMessage", "url": "ws://localhost:9000/p", "options": {"version": 18}},
{"agent": "ReadAllWrite", "url": "ws://localhost:9000/r", "options": {"version": 18}},
{"agent": "CopyFull", "url": "ws://localhost:9000/f", "options": {"version": 18}},
{"agent": "CopyWriterOnly", "url": "ws://localhost:9000/c", "options": {"version": 18}}
],
"cases": ["*"],
"exclude-cases": [],
"exclude-agent-cases": {}
}

View File

@ -1,265 +0,0 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Command server is a test server for the Autobahn WebSockets Test Suite.
package main
import (
"errors"
"flag"
"io"
"log"
"net/http"
"time"
"unicode/utf8"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 4096,
WriteBufferSize: 4096,
EnableCompression: true,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// echoCopy echoes messages from the client using io.Copy.
func echoCopy(w http.ResponseWriter, r *http.Request, writerOnly bool) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Upgrade:", err)
return
}
defer conn.Close()
for {
mt, r, err := conn.NextReader()
if err != nil {
if err != io.EOF {
log.Println("NextReader:", err)
}
return
}
if mt == websocket.TextMessage {
r = &validator{r: r}
}
w, err := conn.NextWriter(mt)
if err != nil {
log.Println("NextWriter:", err)
return
}
if mt == websocket.TextMessage {
r = &validator{r: r}
}
if writerOnly {
_, err = io.Copy(struct{ io.Writer }{w}, r)
} else {
_, err = io.Copy(w, r)
}
if err != nil {
if err == errInvalidUTF8 {
conn.WriteControl(websocket.CloseMessage,
websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""),
time.Time{})
}
log.Println("Copy:", err)
return
}
err = w.Close()
if err != nil {
log.Println("Close:", err)
return
}
}
}
func echoCopyWriterOnly(w http.ResponseWriter, r *http.Request) {
echoCopy(w, r, true)
}
func echoCopyFull(w http.ResponseWriter, r *http.Request) {
echoCopy(w, r, false)
}
// echoReadAll echoes messages from the client by reading the entire message
// with ioutil.ReadAll.
func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage, writePrepared bool) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Upgrade:", err)
return
}
defer conn.Close()
for {
mt, b, err := conn.ReadMessage()
if err != nil {
if err != io.EOF {
log.Println("NextReader:", err)
}
return
}
if mt == websocket.TextMessage {
if !utf8.Valid(b) {
conn.WriteControl(websocket.CloseMessage,
websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""),
time.Time{})
log.Println("ReadAll: invalid utf8")
}
}
if writeMessage {
if !writePrepared {
err = conn.WriteMessage(mt, b)
if err != nil {
log.Println("WriteMessage:", err)
}
} else {
pm, err := websocket.NewPreparedMessage(mt, b)
if err != nil {
log.Println("NewPreparedMessage:", err)
return
}
err = conn.WritePreparedMessage(pm)
if err != nil {
log.Println("WritePreparedMessage:", err)
}
}
} else {
w, err := conn.NextWriter(mt)
if err != nil {
log.Println("NextWriter:", err)
return
}
if _, err := w.Write(b); err != nil {
log.Println("Writer:", err)
return
}
if err := w.Close(); err != nil {
log.Println("Close:", err)
return
}
}
}
}
func echoReadAllWriter(w http.ResponseWriter, r *http.Request) {
echoReadAll(w, r, false, false)
}
func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) {
echoReadAll(w, r, true, false)
}
func echoReadAllWritePreparedMessage(w http.ResponseWriter, r *http.Request) {
echoReadAll(w, r, true, true)
}
func serveHome(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, "Not found.", 404)
return
}
if r.Method != "GET" {
http.Error(w, "Method not allowed", 405)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
io.WriteString(w, "<html><body>Echo Server</body></html>")
}
var addr = flag.String("addr", ":9000", "http service address")
func main() {
flag.Parse()
http.HandleFunc("/", serveHome)
http.HandleFunc("/c", echoCopyWriterOnly)
http.HandleFunc("/f", echoCopyFull)
http.HandleFunc("/r", echoReadAllWriter)
http.HandleFunc("/m", echoReadAllWriteMessage)
http.HandleFunc("/p", echoReadAllWritePreparedMessage)
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
type validator struct {
state int
x rune
r io.Reader
}
var errInvalidUTF8 = errors.New("invalid utf8")
func (r *validator) Read(p []byte) (int, error) {
n, err := r.r.Read(p)
state := r.state
x := r.x
for _, b := range p[:n] {
state, x = decode(state, x, b)
if state == utf8Reject {
break
}
}
r.state = state
r.x = x
if state == utf8Reject || (err == io.EOF && state != utf8Accept) {
return n, errInvalidUTF8
}
return n, err
}
// UTF-8 decoder from http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
//
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
//
// 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.
var utf8d = [...]byte{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df
0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef
0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8
}
const (
utf8Accept = 0
utf8Reject = 1
)
func decode(state int, x rune, b byte) (int, rune) {
t := utf8d[b]
if state != utf8Accept {
x = rune(b&0x3f) | (x << 6)
} else {
x = rune((0xff >> t) & b)
}
state = int(utf8d[256+state*16+int(t)])
return state, x
}

View File

@ -1,102 +0,0 @@
# Chat Example
This application shows how to use use the
[websocket](https://github.com/gorilla/websocket) package to implement a simple
web chat application.
## Running the example
The example requires a working Go development environment. The [Getting
Started](http://golang.org/doc/install) page describes how to install the
development environment.
Once you have Go up and running, you can download, build and run the example
using the following commands.
$ go get github.com/gorilla/websocket
$ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/chat`
$ go run *.go
To use the chat example, open http://localhost:8080/ in your browser.
## Server
The server application defines two types, `Client` and `Hub`. The server
creates an instance of the `Client` type for each websocket connection. A
`Client` acts as an intermediary between the websocket connection and a single
instance of the `Hub` type. The `Hub` maintains a set of registered clients and
broadcasts messages to the clients.
The application runs one goroutine for the `Hub` and two goroutines for each
`Client`. The goroutines communicate with each other using channels. The `Hub`
has channels for registering clients, unregistering clients and broadcasting
messages. A `Client` has a buffered channel of outbound messages. One of the
client's goroutines reads messages from this channel and writes the messages to
the websocket. The other client goroutine reads messages from the websocket and
sends them to the hub.
### Hub
The code for the `Hub` type is in
[hub.go](https://github.com/gorilla/websocket/blob/master/examples/chat/hub.go).
The application's `main` function starts the hub's `run` method as a goroutine.
Clients send requests to the hub using the `register`, `unregister` and
`broadcast` channels.
The hub registers clients by adding the client pointer as a key in the
`clients` map. The map value is always true.
The unregister code is a little more complicated. In addition to deleting the
client pointer from the `clients` map, the hub closes the clients's `send`
channel to signal the client that no more messages will be sent to the client.
The hub handles messages by looping over the registered clients and sending the
message to the client's `send` channel. If the client's `send` buffer is full,
then the hub assumes that the client is dead or stuck. In this case, the hub
unregisters the client and closes the websocket.
### Client
The code for the `Client` type is in [client.go](https://github.com/gorilla/websocket/blob/master/examples/chat/client.go).
The `serveWs` function is registered by the application's `main` function as
an HTTP handler. The handler upgrades the HTTP connection to the WebSocket
protocol, creates a client, registers the client with the hub and schedules the
client to be unregistered using a defer statement.
Next, the HTTP handler starts the client's `writePump` method as a goroutine.
This method transfers messages from the client's send channel to the websocket
connection. The writer method exits when the channel is closed by the hub or
there's an error writing to the websocket connection.
Finally, the HTTP handler calls the client's `readPump` method. This method
transfers inbound messages from the websocket to the hub.
WebSocket connections [support one concurrent reader and one concurrent
writer](https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency). The
application ensures that these concurrency requirements are met by executing
all reads from the `readPump` goroutine and all writes from the `writePump`
goroutine.
To improve efficiency under high load, the `writePump` function coalesces
pending chat messages in the `send` channel to a single WebSocket message. This
reduces the number of system calls and the amount of data sent over the
network.
## Frontend
The frontend code is in [home.html](https://github.com/gorilla/websocket/blob/master/examples/chat/home.html).
On document load, the script checks for websocket functionality in the browser.
If websocket functionality is available, then the script opens a connection to
the server and registers a callback to handle messages from the server. The
callback appends the message to the chat log using the appendLog function.
To allow the user to manually scroll through the chat log without interruption
from new messages, the `appendLog` function checks the scroll position before
adding new content. If the chat log is scrolled to the bottom, then the
function scrolls new content into view after adding the content. Otherwise, the
scroll position is not changed.
The form handler writes the user input to the websocket and clears the input
field.

View File

@ -1,137 +0,0 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"log"
"net/http"
"time"
"github.com/gorilla/websocket"
)
const (
// Time allowed to write a message to the peer.
writeWait = 10 * time.Second
// Time allowed to read the next pong message from the peer.
pongWait = 60 * time.Second
// Send pings to peer with this period. Must be less than pongWait.
pingPeriod = (pongWait * 9) / 10
// Maximum message size allowed from peer.
maxMessageSize = 512
)
var (
newline = []byte{'\n'}
space = []byte{' '}
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
// Client is a middleman between the websocket connection and the hub.
type Client struct {
hub *Hub
// The websocket connection.
conn *websocket.Conn
// Buffered channel of outbound messages.
send chan []byte
}
// readPump pumps messages from the websocket connection to the hub.
//
// The application runs readPump in a per-connection goroutine. The application
// ensures that there is at most one reader on a connection by executing all
// reads from this goroutine.
func (c *Client) readPump() {
defer func() {
c.hub.unregister <- c
c.conn.Close()
}()
c.conn.SetReadLimit(maxMessageSize)
c.conn.SetReadDeadline(time.Now().Add(pongWait))
c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
for {
_, message, err := c.conn.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
log.Printf("error: %v", err)
}
break
}
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
c.hub.broadcast <- message
}
}
// writePump pumps messages from the hub to the websocket connection.
//
// A goroutine running writePump is started for each connection. The
// application ensures that there is at most one writer to a connection by
// executing all writes from this goroutine.
func (c *Client) writePump() {
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
c.conn.Close()
}()
for {
select {
case message, ok := <-c.send:
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
if !ok {
// The hub closed the channel.
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
w, err := c.conn.NextWriter(websocket.TextMessage)
if err != nil {
return
}
w.Write(message)
// Add queued chat messages to the current websocket message.
n := len(c.send)
for i := 0; i < n; i++ {
w.Write(newline)
w.Write(<-c.send)
}
if err := w.Close(); err != nil {
return
}
case <-ticker.C:
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
return
}
}
}
}
// serveWs handles websocket requests from the peer.
func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
client.hub.register <- client
// Allow collection of memory referenced by the caller by doing all work in
// new goroutines.
go client.writePump()
go client.readPump()
}

View File

@ -1,98 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Chat Example</title>
<script type="text/javascript">
window.onload = function () {
var conn;
var msg = document.getElementById("msg");
var log = document.getElementById("log");
function appendLog(item) {
var doScroll = log.scrollTop > log.scrollHeight - log.clientHeight - 1;
log.appendChild(item);
if (doScroll) {
log.scrollTop = log.scrollHeight - log.clientHeight;
}
}
document.getElementById("form").onsubmit = function () {
if (!conn) {
return false;
}
if (!msg.value) {
return false;
}
conn.send(msg.value);
msg.value = "";
return false;
};
if (window["WebSocket"]) {
conn = new WebSocket("ws://" + document.location.host + "/ws");
conn.onclose = function (evt) {
var item = document.createElement("div");
item.innerHTML = "<b>Connection closed.</b>";
appendLog(item);
};
conn.onmessage = function (evt) {
var messages = evt.data.split('\n');
for (var i = 0; i < messages.length; i++) {
var item = document.createElement("div");
item.innerText = messages[i];
appendLog(item);
}
};
} else {
var item = document.createElement("div");
item.innerHTML = "<b>Your browser does not support WebSockets.</b>";
appendLog(item);
}
};
</script>
<style type="text/css">
html {
overflow: hidden;
}
body {
overflow: hidden;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
background: gray;
}
#log {
background: white;
margin: 0;
padding: 0.5em 0.5em 0.5em 0.5em;
position: absolute;
top: 0.5em;
left: 0.5em;
right: 0.5em;
bottom: 3em;
overflow: auto;
}
#form {
padding: 0 0.5em 0 0.5em;
margin: 0;
position: absolute;
bottom: 1em;
left: 0px;
width: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="log"></div>
<form id="form">
<input type="submit" value="Send" />
<input type="text" id="msg" size="64"/>
</form>
</body>
</html>

View File

@ -1,53 +0,0 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
// hub maintains the set of active clients and broadcasts messages to the
// clients.
type Hub struct {
// Registered clients.
clients map[*Client]bool
// Inbound messages from the clients.
broadcast chan []byte
// Register requests from the clients.
register chan *Client
// Unregister requests from clients.
unregister chan *Client
}
func newHub() *Hub {
return &Hub{
broadcast: make(chan []byte),
register: make(chan *Client),
unregister: make(chan *Client),
clients: make(map[*Client]bool),
}
}
func (h *Hub) run() {
for {
select {
case client := <-h.register:
h.clients[client] = true
case client := <-h.unregister:
if _, ok := h.clients[client]; ok {
delete(h.clients, client)
close(client.send)
}
case message := <-h.broadcast:
for client := range h.clients {
select {
case client.send <- message:
default:
close(client.send)
delete(h.clients, client)
}
}
}
}
}

View File

@ -1,40 +0,0 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"flag"
"log"
"net/http"
)
var addr = flag.String("addr", ":8080", "http service address")
func serveHome(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL)
if r.URL.Path != "/" {
http.Error(w, "Not found", 404)
return
}
if r.Method != "GET" {
http.Error(w, "Method not allowed", 405)
return
}
http.ServeFile(w, r, "home.html")
}
func main() {
flag.Parse()
hub := newHub()
go hub.run()
http.HandleFunc("/", serveHome)
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
serveWs(hub, w, r)
})
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}

View File

@ -1,19 +0,0 @@
# Command example
This example connects a websocket connection to stdin and stdout of a command.
Received messages are written to stdin followed by a `\n`. Each line read from
standard out is sent as a message to the client.
$ go get github.com/gorilla/websocket
$ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/command`
$ go run main.go <command and arguments to run>
# Open http://localhost:8080/ .
Try the following commands.
# Echo sent messages to the output area.
$ go run main.go cat
# Run a shell.Try sending "ls" and "cat main.go".
$ go run main.go sh

View File

@ -1,102 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Command Example</title>
<script type="text/javascript">
window.onload = function () {
var conn;
var msg = document.getElementById("msg");
var log = document.getElementById("log");
function appendLog(item) {
var doScroll = log.scrollTop > log.scrollHeight - log.clientHeight - 1;
log.appendChild(item);
if (doScroll) {
log.scrollTop = log.scrollHeight - log.clientHeight;
}
}
document.getElementById("form").onsubmit = function () {
if (!conn) {
return false;
}
if (!msg.value) {
return false;
}
conn.send(msg.value);
msg.value = "";
return false;
};
if (window["WebSocket"]) {
conn = new WebSocket("ws://" + document.location.host + "/ws");
conn.onclose = function (evt) {
var item = document.createElement("div");
item.innerHTML = "<b>Connection closed.</b>";
appendLog(item);
};
conn.onmessage = function (evt) {
var messages = evt.data.split('\n');
for (var i = 0; i < messages.length; i++) {
var item = document.createElement("div");
item.innerText = messages[i];
appendLog(item);
}
};
} else {
var item = document.createElement("div");
item.innerHTML = "<b>Your browser does not support WebSockets.</b>";
appendLog(item);
}
};
</script>
<style type="text/css">
html {
overflow: hidden;
}
body {
overflow: hidden;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
background: gray;
}
#log {
background: white;
margin: 0;
padding: 0.5em 0.5em 0.5em 0.5em;
position: absolute;
top: 0.5em;
left: 0.5em;
right: 0.5em;
bottom: 3em;
overflow: auto;
}
#log pre {
margin: 0;
}
#form {
padding: 0 0.5em 0 0.5em;
margin: 0;
position: absolute;
bottom: 1em;
left: 0px;
width: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="log"></div>
<form id="form">
<input type="submit" value="Send" />
<input type="text" id="msg" size="64"/>
</form>
</body>
</html>

View File

@ -1,193 +0,0 @@
// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"flag"
"io"
"log"
"net/http"
"os"
"os/exec"
"time"
"github.com/gorilla/websocket"
)
var (
addr = flag.String("addr", "127.0.0.1:8080", "http service address")
cmdPath string
)
const (
// Time allowed to write a message to the peer.
writeWait = 10 * time.Second
// Maximum message size allowed from peer.
maxMessageSize = 8192
// Time allowed to read the next pong message from the peer.
pongWait = 60 * time.Second
// Send pings to peer with this period. Must be less than pongWait.
pingPeriod = (pongWait * 9) / 10
// Time to wait before force close on connection.
closeGracePeriod = 10 * time.Second
)
func pumpStdin(ws *websocket.Conn, w io.Writer) {
defer ws.Close()
ws.SetReadLimit(maxMessageSize)
ws.SetReadDeadline(time.Now().Add(pongWait))
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
for {
_, message, err := ws.ReadMessage()
if err != nil {
break
}
message = append(message, '\n')
if _, err := w.Write(message); err != nil {
break
}
}
}
func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) {
defer func() {
}()
s := bufio.NewScanner(r)
for s.Scan() {
ws.SetWriteDeadline(time.Now().Add(writeWait))
if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil {
ws.Close()
break
}
}
if s.Err() != nil {
log.Println("scan:", s.Err())
}
close(done)
ws.SetWriteDeadline(time.Now().Add(writeWait))
ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
time.Sleep(closeGracePeriod)
ws.Close()
}
func ping(ws *websocket.Conn, done chan struct{}) {
ticker := time.NewTicker(pingPeriod)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil {
log.Println("ping:", err)
}
case <-done:
return
}
}
}
func internalError(ws *websocket.Conn, msg string, err error) {
log.Println(msg, err)
ws.WriteMessage(websocket.TextMessage, []byte("Internal server error."))
}
var upgrader = websocket.Upgrader{}
func serveWs(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("upgrade:", err)
return
}
defer ws.Close()
outr, outw, err := os.Pipe()
if err != nil {
internalError(ws, "stdout:", err)
return
}
defer outr.Close()
defer outw.Close()
inr, inw, err := os.Pipe()
if err != nil {
internalError(ws, "stdin:", err)
return
}
defer inr.Close()
defer inw.Close()
proc, err := os.StartProcess(cmdPath, flag.Args(), &os.ProcAttr{
Files: []*os.File{inr, outw, outw},
})
if err != nil {
internalError(ws, "start:", err)
return
}
inr.Close()
outw.Close()
stdoutDone := make(chan struct{})
go pumpStdout(ws, outr, stdoutDone)
go ping(ws, stdoutDone)
pumpStdin(ws, inw)
// Some commands will exit when stdin is closed.
inw.Close()
// Other commands need a bonk on the head.
if err := proc.Signal(os.Interrupt); err != nil {
log.Println("inter:", err)
}
select {
case <-stdoutDone:
case <-time.After(time.Second):
// A bigger bonk on the head.
if err := proc.Signal(os.Kill); err != nil {
log.Println("term:", err)
}
<-stdoutDone
}
if _, err := proc.Wait(); err != nil {
log.Println("wait:", err)
}
}
func serveHome(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, "Not found", 404)
return
}
if r.Method != "GET" {
http.Error(w, "Method not allowed", 405)
return
}
http.ServeFile(w, r, "home.html")
}
func main() {
flag.Parse()
if len(flag.Args()) < 1 {
log.Fatal("must specify at least one argument")
}
var err error
cmdPath, err = exec.LookPath(flag.Args()[0])
if err != nil {
log.Fatal(err)
}
http.HandleFunc("/", serveHome)
http.HandleFunc("/ws", serveWs)
log.Fatal(http.ListenAndServe(*addr, nil))
}

View File

@ -1,17 +0,0 @@
# Client and server example
This example shows a simple client and server.
The server echoes messages sent to it. The client sends a message every second
and prints all messages received.
To run the example, start the server:
$ go run server.go
Next, start the client:
$ go run client.go
The server includes a simple web client. To use the client, open
http://127.0.0.1:8080 in the browser and follow the instructions on the page.

View File

@ -1,81 +0,0 @@
// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
package main
import (
"flag"
"log"
"net/url"
"os"
"os/signal"
"time"
"github.com/gorilla/websocket"
)
var addr = flag.String("addr", "localhost:8080", "http service address")
func main() {
flag.Parse()
log.SetFlags(0)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
log.Printf("connecting to %s", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
done := make(chan struct{})
go func() {
defer c.Close()
defer close(done)
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
return
}
log.Printf("recv: %s", message)
}
}()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case t := <-ticker.C:
err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
if err != nil {
log.Println("write:", err)
return
}
case <-interrupt:
log.Println("interrupt")
// To cleanly close a connection, a client should send a close
// frame and wait for the server to close the connection.
err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("write close:", err)
return
}
select {
case <-done:
case <-time.After(time.Second):
}
c.Close()
return
}
}
}

View File

@ -1,132 +0,0 @@
// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
package main
import (
"flag"
"html/template"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var addr = flag.String("addr", "localhost:8080", "http service address")
var upgrader = websocket.Upgrader{} // use default options
func echo(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer c.Close()
for {
mt, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", message)
err = c.WriteMessage(mt, message)
if err != nil {
log.Println("write:", err)
break
}
}
}
func home(w http.ResponseWriter, r *http.Request) {
homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}
func main() {
flag.Parse()
log.SetFlags(0)
http.HandleFunc("/echo", echo)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe(*addr, nil))
}
var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script>
window.addEventListener("load", function(evt) {
var output = document.getElementById("output");
var input = document.getElementById("input");
var ws;
var print = function(message) {
var d = document.createElement("div");
d.innerHTML = message;
output.appendChild(d);
};
document.getElementById("open").onclick = function(evt) {
if (ws) {
return false;
}
ws = new WebSocket("{{.}}");
ws.onopen = function(evt) {
print("OPEN");
}
ws.onclose = function(evt) {
print("CLOSE");
ws = null;
}
ws.onmessage = function(evt) {
print("RESPONSE: " + evt.data);
}
ws.onerror = function(evt) {
print("ERROR: " + evt.data);
}
return false;
};
document.getElementById("send").onclick = function(evt) {
if (!ws) {
return false;
}
print("SEND: " + input.value);
ws.send(input.value);
return false;
};
document.getElementById("close").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
return false;
};
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server,
"Send" to send a message to the server and "Close" to close the connection.
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<p><input id="input" type="text" value="Hello world!">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output"></div>
</td></tr></table>
</body>
</html>
`))

View File

@ -1,9 +0,0 @@
# File Watch example.
This example sends a file to the browser client for display whenever the file is modified.
$ go get github.com/gorilla/websocket
$ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/filewatch`
$ go run main.go <name of file to watch>
# Open http://localhost:8080/ .
# Modify the file to see it update in the browser.

View File

@ -1,193 +0,0 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"flag"
"html/template"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
"github.com/gorilla/websocket"
)
const (
// Time allowed to write the file to the client.
writeWait = 10 * time.Second
// Time allowed to read the next pong message from the client.
pongWait = 60 * time.Second
// Send pings to client with this period. Must be less than pongWait.
pingPeriod = (pongWait * 9) / 10
// Poll file for changes with this period.
filePeriod = 10 * time.Second
)
var (
addr = flag.String("addr", ":8080", "http service address")
homeTempl = template.Must(template.New("").Parse(homeHTML))
filename string
upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
)
func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) {
fi, err := os.Stat(filename)
if err != nil {
return nil, lastMod, err
}
if !fi.ModTime().After(lastMod) {
return nil, lastMod, nil
}
p, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fi.ModTime(), err
}
return p, fi.ModTime(), nil
}
func reader(ws *websocket.Conn) {
defer ws.Close()
ws.SetReadLimit(512)
ws.SetReadDeadline(time.Now().Add(pongWait))
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
for {
_, _, err := ws.ReadMessage()
if err != nil {
break
}
}
}
func writer(ws *websocket.Conn, lastMod time.Time) {
lastError := ""
pingTicker := time.NewTicker(pingPeriod)
fileTicker := time.NewTicker(filePeriod)
defer func() {
pingTicker.Stop()
fileTicker.Stop()
ws.Close()
}()
for {
select {
case <-fileTicker.C:
var p []byte
var err error
p, lastMod, err = readFileIfModified(lastMod)
if err != nil {
if s := err.Error(); s != lastError {
lastError = s
p = []byte(lastError)
}
} else {
lastError = ""
}
if p != nil {
ws.SetWriteDeadline(time.Now().Add(writeWait))
if err := ws.WriteMessage(websocket.TextMessage, p); err != nil {
return
}
}
case <-pingTicker.C:
ws.SetWriteDeadline(time.Now().Add(writeWait))
if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
return
}
}
}
}
func serveWs(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
if _, ok := err.(websocket.HandshakeError); !ok {
log.Println(err)
}
return
}
var lastMod time.Time
if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err == nil {
lastMod = time.Unix(0, n)
}
go writer(ws, lastMod)
reader(ws)
}
func serveHome(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, "Not found", 404)
return
}
if r.Method != "GET" {
http.Error(w, "Method not allowed", 405)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
p, lastMod, err := readFileIfModified(time.Time{})
if err != nil {
p = []byte(err.Error())
lastMod = time.Unix(0, 0)
}
var v = struct {
Host string
Data string
LastMod string
}{
r.Host,
string(p),
strconv.FormatInt(lastMod.UnixNano(), 16),
}
homeTempl.Execute(w, &v)
}
func main() {
flag.Parse()
if flag.NArg() != 1 {
log.Fatal("filename not specified")
}
filename = flag.Args()[0]
http.HandleFunc("/", serveHome)
http.HandleFunc("/ws", serveWs)
if err := http.ListenAndServe(*addr, nil); err != nil {
log.Fatal(err)
}
}
const homeHTML = `<!DOCTYPE html>
<html lang="en">
<head>
<title>WebSocket Example</title>
</head>
<body>
<pre id="fileData">{{.Data}}</pre>
<script type="text/javascript">
(function() {
var data = document.getElementById("fileData");
var conn = new WebSocket("ws://{{.Host}}/ws?lastMod={{.LastMod}}");
conn.onclose = function(evt) {
data.textContent = 'Connection closed';
}
conn.onmessage = function(evt) {
console.log('file updated');
data.textContent = evt.data;
}
})();
</script>
</body>
</html>
`

View File

@ -1,119 +0,0 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bytes"
"encoding/json"
"io"
"reflect"
"testing"
)
func TestJSON(t *testing.T) {
var buf bytes.Buffer
c := fakeNetConn{&buf, &buf}
wc := newConn(c, true, 1024, 1024)
rc := newConn(c, false, 1024, 1024)
var actual, expect struct {
A int
B string
}
expect.A = 1
expect.B = "hello"
if err := wc.WriteJSON(&expect); err != nil {
t.Fatal("write", err)
}
if err := rc.ReadJSON(&actual); err != nil {
t.Fatal("read", err)
}
if !reflect.DeepEqual(&actual, &expect) {
t.Fatal("equal", actual, expect)
}
}
func TestPartialJSONRead(t *testing.T) {
var buf bytes.Buffer
c := fakeNetConn{&buf, &buf}
wc := newConn(c, true, 1024, 1024)
rc := newConn(c, false, 1024, 1024)
var v struct {
A int
B string
}
v.A = 1
v.B = "hello"
messageCount := 0
// Partial JSON values.
data, err := json.Marshal(v)
if err != nil {
t.Fatal(err)
}
for i := len(data) - 1; i >= 0; i-- {
if err := wc.WriteMessage(TextMessage, data[:i]); err != nil {
t.Fatal(err)
}
messageCount++
}
// Whitespace.
if err := wc.WriteMessage(TextMessage, []byte(" ")); err != nil {
t.Fatal(err)
}
messageCount++
// Close.
if err := wc.WriteMessage(CloseMessage, FormatCloseMessage(CloseNormalClosure, "")); err != nil {
t.Fatal(err)
}
for i := 0; i < messageCount; i++ {
err := rc.ReadJSON(&v)
if err != io.ErrUnexpectedEOF {
t.Error("read", i, err)
}
}
err = rc.ReadJSON(&v)
if _, ok := err.(*CloseError); !ok {
t.Error("final", err)
}
}
func TestDeprecatedJSON(t *testing.T) {
var buf bytes.Buffer
c := fakeNetConn{&buf, &buf}
wc := newConn(c, true, 1024, 1024)
rc := newConn(c, false, 1024, 1024)
var actual, expect struct {
A int
B string
}
expect.A = 1
expect.B = "hello"
if err := WriteJSON(wc, &expect); err != nil {
t.Fatal("write", err)
}
if err := ReadJSON(rc, &actual); err != nil {
t.Fatal("read", err)
}
if !reflect.DeepEqual(&actual, &expect) {
t.Fatal("equal", actual, expect)
}
}

View File

@ -1,73 +0,0 @@
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in the
// LICENSE file.
// Require 1.7 for sub-bencmarks
// +build go1.7,!appengine
package websocket
import (
"fmt"
"testing"
)
func maskBytesByByte(key [4]byte, pos int, b []byte) int {
for i := range b {
b[i] ^= key[pos&3]
pos++
}
return pos & 3
}
func notzero(b []byte) int {
for i := range b {
if b[i] != 0 {
return i
}
}
return -1
}
func TestMaskBytes(t *testing.T) {
key := [4]byte{1, 2, 3, 4}
for size := 1; size <= 1024; size++ {
for align := 0; align < wordSize; align++ {
for pos := 0; pos < 4; pos++ {
b := make([]byte, size+align)[align:]
maskBytes(key, pos, b)
maskBytesByByte(key, pos, b)
if i := notzero(b); i >= 0 {
t.Errorf("size:%d, align:%d, pos:%d, offset:%d", size, align, pos, i)
}
}
}
}
}
func BenchmarkMaskBytes(b *testing.B) {
for _, size := range []int{2, 4, 8, 16, 32, 512, 1024} {
b.Run(fmt.Sprintf("size-%d", size), func(b *testing.B) {
for _, align := range []int{wordSize / 2} {
b.Run(fmt.Sprintf("align-%d", align), func(b *testing.B) {
for _, fn := range []struct {
name string
fn func(key [4]byte, pos int, b []byte) int
}{
{"byte", maskBytesByByte},
{"word", maskBytes},
} {
b.Run(fn.name, func(b *testing.B) {
key := newMaskKey()
data := make([]byte, size+align)[align:]
for i := 0; i < b.N; i++ {
fn.fn(key, 0, data)
}
b.SetBytes(int64(len(data)))
})
}
})
}
})
}
}

View File

@ -1,74 +0,0 @@
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bytes"
"compress/flate"
"math/rand"
"testing"
)
var preparedMessageTests = []struct {
messageType int
isServer bool
enableWriteCompression bool
compressionLevel int
}{
// Server
{TextMessage, true, false, flate.BestSpeed},
{TextMessage, true, true, flate.BestSpeed},
{TextMessage, true, true, flate.BestCompression},
{PingMessage, true, false, flate.BestSpeed},
{PingMessage, true, true, flate.BestSpeed},
// Client
{TextMessage, false, false, flate.BestSpeed},
{TextMessage, false, true, flate.BestSpeed},
{TextMessage, false, true, flate.BestCompression},
{PingMessage, false, false, flate.BestSpeed},
{PingMessage, false, true, flate.BestSpeed},
}
func TestPreparedMessage(t *testing.T) {
for _, tt := range preparedMessageTests {
var data = []byte("this is a test")
var buf bytes.Buffer
c := newConn(fakeNetConn{Reader: nil, Writer: &buf}, tt.isServer, 1024, 1024)
if tt.enableWriteCompression {
c.newCompressionWriter = compressNoContextTakeover
}
c.SetCompressionLevel(tt.compressionLevel)
// Seed random number generator for consistent frame mask.
rand.Seed(1234)
if err := c.WriteMessage(tt.messageType, data); err != nil {
t.Fatal(err)
}
want := buf.String()
pm, err := NewPreparedMessage(tt.messageType, data)
if err != nil {
t.Fatal(err)
}
// Scribble on data to ensure that NewPreparedMessage takes a snapshot.
copy(data, "hello world")
// Seed random number generator for consistent frame mask.
rand.Seed(1234)
buf.Reset()
if err := c.WritePreparedMessage(pm); err != nil {
t.Fatal(err)
}
got := buf.String()
if got != want {
t.Errorf("write message != prepared message for %+v", tt)
}
}
}

View File

@ -1,51 +0,0 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"net/http"
"reflect"
"testing"
)
var subprotocolTests = []struct {
h string
protocols []string
}{
{"", nil},
{"foo", []string{"foo"}},
{"foo,bar", []string{"foo", "bar"}},
{"foo, bar", []string{"foo", "bar"}},
{" foo, bar", []string{"foo", "bar"}},
{" foo, bar ", []string{"foo", "bar"}},
}
func TestSubprotocols(t *testing.T) {
for _, st := range subprotocolTests {
r := http.Request{Header: http.Header{"Sec-Websocket-Protocol": {st.h}}}
protocols := Subprotocols(&r)
if !reflect.DeepEqual(st.protocols, protocols) {
t.Errorf("SubProtocols(%q) returned %#v, want %#v", st.h, protocols, st.protocols)
}
}
}
var isWebSocketUpgradeTests = []struct {
ok bool
h http.Header
}{
{false, http.Header{"Upgrade": {"websocket"}}},
{false, http.Header{"Connection": {"upgrade"}}},
{true, http.Header{"Connection": {"upgRade"}, "Upgrade": {"WebSocket"}}},
}
func TestIsWebSocketUpgrade(t *testing.T) {
for _, tt := range isWebSocketUpgradeTests {
ok := IsWebSocketUpgrade(&http.Request{Header: tt.h})
if tt.ok != ok {
t.Errorf("IsWebSocketUpgrade(%v) returned %v, want %v", tt.h, ok, tt.ok)
}
}
}

View File

@ -1,74 +0,0 @@
// Copyright 2014 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"net/http"
"reflect"
"testing"
)
var tokenListContainsValueTests = []struct {
value string
ok bool
}{
{"WebSocket", true},
{"WEBSOCKET", true},
{"websocket", true},
{"websockets", false},
{"x websocket", false},
{"websocket x", false},
{"other,websocket,more", true},
{"other, websocket, more", true},
}
func TestTokenListContainsValue(t *testing.T) {
for _, tt := range tokenListContainsValueTests {
h := http.Header{"Upgrade": {tt.value}}
ok := tokenListContainsValue(h, "Upgrade", "websocket")
if ok != tt.ok {
t.Errorf("tokenListContainsValue(h, n, %q) = %v, want %v", tt.value, ok, tt.ok)
}
}
}
var parseExtensionTests = []struct {
value string
extensions []map[string]string
}{
{`foo`, []map[string]string{map[string]string{"": "foo"}}},
{`foo, bar; baz=2`, []map[string]string{
map[string]string{"": "foo"},
map[string]string{"": "bar", "baz": "2"}}},
{`foo; bar="b,a;z"`, []map[string]string{
map[string]string{"": "foo", "bar": "b,a;z"}}},
{`foo , bar; baz = 2`, []map[string]string{
map[string]string{"": "foo"},
map[string]string{"": "bar", "baz": "2"}}},
{`foo, bar; baz=2 junk`, []map[string]string{
map[string]string{"": "foo"}}},
{`foo junk, bar; baz=2 junk`, nil},
{`mux; max-channels=4; flow-control, deflate-stream`, []map[string]string{
map[string]string{"": "mux", "max-channels": "4", "flow-control": ""},
map[string]string{"": "deflate-stream"}}},
{`permessage-foo; x="10"`, []map[string]string{
map[string]string{"": "permessage-foo", "x": "10"}}},
{`permessage-foo; use_y, permessage-foo`, []map[string]string{
map[string]string{"": "permessage-foo", "use_y": ""},
map[string]string{"": "permessage-foo"}}},
{`permessage-deflate; client_max_window_bits; server_max_window_bits=10 , permessage-deflate; client_max_window_bits`, []map[string]string{
map[string]string{"": "permessage-deflate", "client_max_window_bits": "", "server_max_window_bits": "10"},
map[string]string{"": "permessage-deflate", "client_max_window_bits": ""}}},
}
func TestParseExtensions(t *testing.T) {
for _, tt := range parseExtensionTests {
h := http.Header{http.CanonicalHeaderKey("Sec-WebSocket-Extensions"): {tt.value}}
extensions := parseExtensions(h)
if !reflect.DeepEqual(extensions, tt.extensions) {
t.Errorf("parseExtensions(%q)\n = %v,\nwant %v", tt.value, extensions, tt.extensions)
}
}
}

15
vendor/modules.txt vendored Normal file
View File

@ -0,0 +1,15 @@
# code.ivysaur.me/libnmdc v0.16.0
code.ivysaur.me/libnmdc
# github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7
github.com/cxmcc/tiger
# github.com/googollee/go-engine.io v0.0.0-20170224222511-80ae0e43aca1
github.com/googollee/go-engine.io
github.com/googollee/go-engine.io/message
github.com/googollee/go-engine.io/parser
github.com/googollee/go-engine.io/polling
github.com/googollee/go-engine.io/transport
github.com/googollee/go-engine.io/websocket
# github.com/googollee/go-socket.io v0.0.0-20170525141029-5447e71f36d3
github.com/googollee/go-socket.io
# github.com/gorilla/websocket v1.2.0
github.com/gorilla/websocket