Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
go-libp2p
Commits
8acc21e8
Commit
8acc21e8
authored
Nov 16, 2015
by
Jeromy
Browse files
Vendor in go-peerstream
parent
a9de494f
Changes
177
Hide whitespace changes
Inline
Side-by-side
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/client_test.go
0 → 100644
View file @
8acc21e8
// 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
}{
{
"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"
}},
{
"ss://example.com/a/b"
,
nil
},
{
"ws://webmaster@example.com/"
,
nil
},
}
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
&&
err
==
nil
{
t
.
Errorf
(
"parseURL(%q) did not return error"
,
tt
.
s
)
continue
}
if
!
reflect
.
DeepEqual
(
u
,
tt
.
u
)
{
t
.
Errorf
(
"parseURL(%q) returned %v, want %v"
,
tt
.
s
,
u
,
tt
.
u
)
continue
}
}
}
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
)
}
}
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/conn.go
0 → 100644
View file @
8acc21e8
// 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"
"encoding/binary"
"errors"
"io"
"io/ioutil"
"math/rand"
"net"
"strconv"
"time"
)
const
(
maxFrameHeaderSize
=
2
+
8
+
4
// Fixed header + length + mask
maxControlFramePayloadSize
=
125
finalBit
=
1
<<
7
maskBit
=
1
<<
7
writeWait
=
time
.
Second
defaultReadBufferSize
=
4096
defaultWriteBufferSize
=
4096
continuationFrame
=
0
noFrame
=
-
1
)
// Close codes defined in RFC 6455, section 11.7.
const
(
CloseNormalClosure
=
1000
CloseGoingAway
=
1001
CloseProtocolError
=
1002
CloseUnsupportedData
=
1003
CloseNoStatusReceived
=
1005
CloseAbnormalClosure
=
1006
CloseInvalidFramePayloadData
=
1007
ClosePolicyViolation
=
1008
CloseMessageTooBig
=
1009
CloseMandatoryExtension
=
1010
CloseInternalServerErr
=
1011
CloseTLSHandshake
=
1015
)
// The message types are defined in RFC 6455, section 11.8.
const
(
// TextMessage denotes a text data message. The text message payload is
// interpreted as UTF-8 encoded text data.
TextMessage
=
1
// BinaryMessage denotes a binary data message.
BinaryMessage
=
2
// CloseMessage denotes a close control message. The optional message
// payload contains a numeric code and text. Use the FormatCloseMessage
// function to format a close message payload.
CloseMessage
=
8
// PingMessage denotes a ping control message. The optional message payload
// is UTF-8 encoded text.
PingMessage
=
9
// PongMessage denotes a ping control message. The optional message payload
// is UTF-8 encoded text.
PongMessage
=
10
)
// ErrCloseSent is returned when the application writes a message to the
// connection after sending a close message.
var
ErrCloseSent
=
errors
.
New
(
"websocket: close sent"
)
// ErrReadLimit is returned when reading a message that is larger than the
// read limit set for the connection.
var
ErrReadLimit
=
errors
.
New
(
"websocket: read limit exceeded"
)
// netError satisfies the net Error interface.
type
netError
struct
{
msg
string
temporary
bool
timeout
bool
}
func
(
e
*
netError
)
Error
()
string
{
return
e
.
msg
}
func
(
e
*
netError
)
Temporary
()
bool
{
return
e
.
temporary
}
func
(
e
*
netError
)
Timeout
()
bool
{
return
e
.
timeout
}
// CloseError represents close frame.
type
CloseError
struct
{
// Code is defined in RFC 6455, section 11.7.
Code
int
// Text is the optional text payload.
Text
string
}
func
(
e
*
CloseError
)
Error
()
string
{
return
"websocket: close "
+
strconv
.
Itoa
(
e
.
Code
)
+
" "
+
e
.
Text
}
var
(
errWriteTimeout
=
&
netError
{
msg
:
"websocket: write timeout"
,
timeout
:
true
,
temporary
:
true
}
errUnexpectedEOF
=
&
CloseError
{
Code
:
CloseAbnormalClosure
,
Text
:
io
.
ErrUnexpectedEOF
.
Error
()}
errBadWriteOpCode
=
errors
.
New
(
"websocket: bad write message type"
)
errWriteClosed
=
errors
.
New
(
"websocket: write closed"
)
errInvalidControlFrame
=
errors
.
New
(
"websocket: invalid control frame"
)
)
func
hideTempErr
(
err
error
)
error
{
if
e
,
ok
:=
err
.
(
net
.
Error
);
ok
&&
e
.
Temporary
()
{
err
=
&
netError
{
msg
:
e
.
Error
(),
timeout
:
e
.
Timeout
()}
}
return
err
}
func
isControl
(
frameType
int
)
bool
{
return
frameType
==
CloseMessage
||
frameType
==
PingMessage
||
frameType
==
PongMessage
}
func
isData
(
frameType
int
)
bool
{
return
frameType
==
TextMessage
||
frameType
==
BinaryMessage
}
func
maskBytes
(
key
[
4
]
byte
,
pos
int
,
b
[]
byte
)
int
{
for
i
:=
range
b
{
b
[
i
]
^=
key
[
pos
&
3
]
pos
++
}
return
pos
&
3
}
func
newMaskKey
()
[
4
]
byte
{
n
:=
rand
.
Uint32
()
return
[
4
]
byte
{
byte
(
n
),
byte
(
n
>>
8
),
byte
(
n
>>
16
),
byte
(
n
>>
24
)}
}
// Conn represents a WebSocket connection.
type
Conn
struct
{
conn
net
.
Conn
isServer
bool
subprotocol
string
// Write fields
mu
chan
bool
// used as mutex to protect write to conn and closeSent
closeSent
bool
// true if close message was sent
// Message writer fields.
writeErr
error
writeBuf
[]
byte
// frame is constructed in this buffer.
writePos
int
// end of data in writeBuf.
writeFrameType
int
// type of the current frame.
writeSeq
int
// incremented to invalidate message writers.
writeDeadline
time
.
Time
// Read fields
readErr
error
br
*
bufio
.
Reader
readRemaining
int64
// bytes remaining in current frame.
readFinal
bool
// true the current message has more frames.
readSeq
int
// incremented to invalidate message readers.
readLength
int64
// Message size.
readLimit
int64
// Maximum message size.
readMaskPos
int
readMaskKey
[
4
]
byte
handlePong
func
(
string
)
error
handlePing
func
(
string
)
error
}
func
newConn
(
conn
net
.
Conn
,
isServer
bool
,
readBufferSize
,
writeBufferSize
int
)
*
Conn
{
mu
:=
make
(
chan
bool
,
1
)
mu
<-
true
if
readBufferSize
==
0
{
readBufferSize
=
defaultReadBufferSize
}
if
writeBufferSize
==
0
{
writeBufferSize
=
defaultWriteBufferSize
}
c
:=
&
Conn
{
isServer
:
isServer
,
br
:
bufio
.
NewReaderSize
(
conn
,
readBufferSize
),
conn
:
conn
,
mu
:
mu
,
readFinal
:
true
,
writeBuf
:
make
([]
byte
,
writeBufferSize
+
maxFrameHeaderSize
),
writeFrameType
:
noFrame
,
writePos
:
maxFrameHeaderSize
,
}
c
.
SetPingHandler
(
nil
)
c
.
SetPongHandler
(
nil
)
return
c
}
// Subprotocol returns the negotiated protocol for the connection.
func
(
c
*
Conn
)
Subprotocol
()
string
{
return
c
.
subprotocol
}
// Close closes the underlying network connection without sending or waiting for a close frame.
func
(
c
*
Conn
)
Close
()
error
{
return
c
.
conn
.
Close
()
}
// LocalAddr returns the local network address.
func
(
c
*
Conn
)
LocalAddr
()
net
.
Addr
{
return
c
.
conn
.
LocalAddr
()
}
// RemoteAddr returns the remote network address.
func
(
c
*
Conn
)
RemoteAddr
()
net
.
Addr
{
return
c
.
conn
.
RemoteAddr
()
}
// Write methods
func
(
c
*
Conn
)
write
(
frameType
int
,
deadline
time
.
Time
,
bufs
...
[]
byte
)
error
{
<-
c
.
mu
defer
func
()
{
c
.
mu
<-
true
}()
if
c
.
closeSent
{
return
ErrCloseSent
}
else
if
frameType
==
CloseMessage
{
c
.
closeSent
=
true
}
c
.
conn
.
SetWriteDeadline
(
deadline
)
for
_
,
buf
:=
range
bufs
{
if
len
(
buf
)
>
0
{
n
,
err
:=
c
.
conn
.
Write
(
buf
)
if
n
!=
len
(
buf
)
{
// Close on partial write.
c
.
conn
.
Close
()
}
if
err
!=
nil
{
return
err
}
}
}
return
nil
}
// WriteControl writes a control message with the given deadline. The allowed
// message types are CloseMessage, PingMessage and PongMessage.
func
(
c
*
Conn
)
WriteControl
(
messageType
int
,
data
[]
byte
,
deadline
time
.
Time
)
error
{
if
!
isControl
(
messageType
)
{
return
errBadWriteOpCode
}
if
len
(
data
)
>
maxControlFramePayloadSize
{
return
errInvalidControlFrame
}
b0
:=
byte
(
messageType
)
|
finalBit
b1
:=
byte
(
len
(
data
))
if
!
c
.
isServer
{
b1
|=
maskBit
}
buf
:=
make
([]
byte
,
0
,
maxFrameHeaderSize
+
maxControlFramePayloadSize
)
buf
=
append
(
buf
,
b0
,
b1
)
if
c
.
isServer
{
buf
=
append
(
buf
,
data
...
)
}
else
{
key
:=
newMaskKey
()
buf
=
append
(
buf
,
key
[
:
]
...
)
buf
=
append
(
buf
,
data
...
)
maskBytes
(
key
,
0
,
buf
[
6
:
])
}
d
:=
time
.
Hour
*
1000
if
!
deadline
.
IsZero
()
{
d
=
deadline
.
Sub
(
time
.
Now
())
if
d
<
0
{
return
errWriteTimeout
}
}
timer
:=
time
.
NewTimer
(
d
)
select
{
case
<-
c
.
mu
:
timer
.
Stop
()
case
<-
timer
.
C
:
return
errWriteTimeout
}
defer
func
()
{
c
.
mu
<-
true
}()
if
c
.
closeSent
{
return
ErrCloseSent
}
else
if
messageType
==
CloseMessage
{
c
.
closeSent
=
true
}
c
.
conn
.
SetWriteDeadline
(
deadline
)
n
,
err
:=
c
.
conn
.
Write
(
buf
)
if
n
!=
0
&&
n
!=
len
(
buf
)
{
c
.
conn
.
Close
()
}
return
hideTempErr
(
err
)
}
// NextWriter returns a writer for the next message to send. The writer's
// Close method flushes the complete message to the network.
//
// There can be at most one open writer on a connection. NextWriter closes the
// previous writer if the application has not already done so.
//
// The NextWriter method and the writers returned from the method cannot be
// accessed by more than one goroutine at a time.
func
(
c
*
Conn
)
NextWriter
(
messageType
int
)
(
io
.
WriteCloser
,
error
)
{
if
c
.
writeErr
!=
nil
{
return
nil
,
c
.
writeErr
}
if
c
.
writeFrameType
!=
noFrame
{
if
err
:=
c
.
flushFrame
(
true
,
nil
);
err
!=
nil
{
return
nil
,
err
}
}
if
!
isControl
(
messageType
)
&&
!
isData
(
messageType
)
{
return
nil
,
errBadWriteOpCode
}
c
.
writeFrameType
=
messageType
return
messageWriter
{
c
,
c
.
writeSeq
},
nil
}
func
(
c
*
Conn
)
flushFrame
(
final
bool
,
extra
[]
byte
)
error
{
length
:=
c
.
writePos
-
maxFrameHeaderSize
+
len
(
extra
)
// Check for invalid control frames.
if
isControl
(
c
.
writeFrameType
)
&&
(
!
final
||
length
>
maxControlFramePayloadSize
)
{
c
.
writeSeq
++
c
.
writeFrameType
=
noFrame
c
.
writePos
=
maxFrameHeaderSize
return
errInvalidControlFrame
}
b0
:=
byte
(
c
.
writeFrameType
)
if
final
{
b0
|=
finalBit
}
b1
:=
byte
(
0
)
if
!
c
.
isServer
{
b1
|=
maskBit
}
// Assume that the frame starts at beginning of c.writeBuf.
framePos
:=
0
if
c
.
isServer
{
// Adjust up if mask not included in the header.
framePos
=
4
}
switch
{
case
length
>=
65536
:
c
.
writeBuf
[
framePos
]
=
b0
c
.
writeBuf
[
framePos
+
1
]
=
b1
|
127
binary
.
BigEndian
.
PutUint64
(
c
.
writeBuf
[
framePos
+
2
:
],
uint64
(
length
))
case
length
>
125
:
framePos
+=
6
c
.
writeBuf
[
framePos
]
=
b0
c
.
writeBuf
[
framePos
+
1
]
=
b1
|
126
binary
.
BigEndian
.
PutUint16
(
c
.
writeBuf
[
framePos
+
2
:
],
uint16
(
length
))
default
:
framePos
+=
8
c
.
writeBuf
[
framePos
]
=
b0
c
.
writeBuf
[
framePos
+
1
]
=
b1
|
byte
(
length
)
}
if
!
c
.
isServer
{
key
:=
newMaskKey
()
copy
(
c
.
writeBuf
[
maxFrameHeaderSize
-
4
:
],
key
[
:
])
maskBytes
(
key
,
0
,
c
.
writeBuf
[
maxFrameHeaderSize
:
c
.
writePos
])
if
len
(
extra
)
>
0
{
c
.
writeErr
=
errors
.
New
(
"websocket: internal error, extra used in client mode"
)
return
c
.
writeErr
}
}
// Write the buffers to the connection.
c
.
writeErr
=
c
.
write
(
c
.
writeFrameType
,
c
.
writeDeadline
,
c
.
writeBuf
[
framePos
:
c
.
writePos
],
extra
)
// Setup for next frame.
c
.
writePos
=
maxFrameHeaderSize
c
.
writeFrameType
=
continuationFrame
if
final
{
c
.
writeSeq
++
c
.
writeFrameType
=
noFrame
}
return
c
.
writeErr
}
type
messageWriter
struct
{
c
*
Conn
seq
int
}
func
(
w
messageWriter
)
err
()
error
{
c
:=
w
.
c
if
c
.
writeSeq
!=
w
.
seq
{
return
errWriteClosed
}
if
c
.
writeErr
!=
nil
{
return
c
.
writeErr
}
return
nil
}
func
(
w
messageWriter
)
ncopy
(
max
int
)
(
int
,
error
)
{
n
:=
len
(
w
.
c
.
writeBuf
)
-
w
.
c
.
writePos
if
n
<=
0
{
if
err
:=
w
.
c
.
flushFrame
(
false
,
nil
);
err
!=
nil
{
return
0
,
err
}
n
=
len
(
w
.
c
.
writeBuf
)
-
w
.
c
.
writePos
}
if
n
>
max
{
n
=
max
}
return
n
,
nil
}
func
(
w
messageWriter
)
write
(
final
bool
,
p
[]
byte
)
(
int
,
error
)
{
if
err
:=
w
.
err
();
err
!=
nil
{
return
0
,
err
}
if
len
(
p
)
>
2
*
len
(
w
.
c
.
writeBuf
)
&&
w
.
c
.
isServer
{
// Don't buffer large messages.
err
:=
w
.
c
.
flushFrame
(
final
,
p
)
if
err
!=
nil
{
return
0
,
err
}
return
len
(
p
),
nil
}
nn
:=
len
(
p
)
for
len
(
p
)
>
0
{
n
,
err
:=
w
.
ncopy
(
len
(
p
))
if
err
!=
nil
{
return
0
,
err
}
copy
(
w
.
c
.
writeBuf
[
w
.
c
.
writePos
:
],
p
[
:
n
])
w
.
c
.
writePos
+=
n
p
=
p
[
n
:
]
}
return
nn
,
nil
}
func
(
w
messageWriter
)
Write
(
p
[]
byte
)
(
int
,
error
)
{
return
w
.
write
(
false
,
p
)
}
func
(
w
messageWriter
)
WriteString
(
p
string
)
(
int
,
error
)
{
if
err
:=
w
.
err
();
err
!=
nil
{
return
0
,
err
}
nn
:=
len
(
p
)
for
len
(
p
)
>
0
{
n
,
err
:=
w
.
ncopy
(
len
(
p
))
if
err
!=
nil
{
return
0
,
err
}
copy
(
w
.
c
.
writeBuf
[
w
.
c
.
writePos
:
],
p
[
:
n
])
w
.
c
.
writePos
+=
n
p
=
p
[
n
:
]
}
return
nn
,
nil
}
func
(
w
messageWriter
)
ReadFrom
(
r
io
.
Reader
)
(
nn
int64
,
err
error
)
{
if
err
:=
w
.
err
();
err
!=
nil
{
return
0
,
err
}
for
{
if
w
.
c
.
writePos
==
len
(
w
.
c
.
writeBuf
)
{
err
=
w
.
c
.
flushFrame
(
false
,
nil
)
if
err
!=
nil
{
break
}
}
var
n
int
n
,
err
=
r
.
Read
(
w
.
c
.
writeBuf
[
w
.
c
.
writePos
:
])
w
.
c
.
writePos
+=
n
nn
+=
int64
(
n
)
if
err
!=
nil
{
if
err
==
io
.
EOF
{
err
=
nil
}
break
}
}
return
nn
,
err
}
func
(
w
messageWriter
)
Close
()
error
{
if
err
:=
w
.
err
();
err
!=
nil
{
return
err
}
return
w
.
c
.
flushFrame
(
true
,
nil
)
}
// WriteMessage is a helper method for getting a writer using NextWriter,
// writing the message and closing the writer.
func
(
c
*
Conn
)
WriteMessage
(
messageType
int
,
data
[]
byte
)
error
{
wr
,
err
:=
c
.
NextWriter
(
messageType
)
if
err
!=
nil
{
return
err
}
w
:=
wr
.
(
messageWriter
)
if
_
,
err
:=
w
.
write
(
true
,
data
);
err
!=
nil
{
return
err
}
if
c
.
writeSeq
==
w
.
seq
{
if
err
:=
c
.
flushFrame
(
true
,
nil
);
err
!=
nil
{
return
err
}
}
return
nil
}
// SetWriteDeadline sets the write deadline on the underlying network
// connection. After a write has timed out, the websocket state is corrupt and
// all future writes will return an error. A zero value for t means writes will
// not time out.
func
(
c
*
Conn
)
SetWriteDeadline
(
t
time
.
Time
)
error
{
c
.
writeDeadline
=
t
return
nil
}
// Read methods
// readFull is like io.ReadFull except that io.EOF is never returned.
func
(
c
*
Conn
)
readFull
(
p
[]
byte
)
(
err
error
)
{
var
n
int
for
n
<
len
(
p
)
&&
err
==
nil
{
var
nn
int
nn
,
err
=
c
.
br
.
Read
(
p
[
n
:
])
n
+=
nn
}
if
n
==
len
(
p
)
{
err
=
nil
}
else
if
err
==
io
.
EOF
{
err
=
errUnexpectedEOF
}
return
}
func
(
c
*
Conn
)
advanceFrame
()
(
int
,
error
)
{
// 1. Skip remainder of previous frame.
if
c
.
readRemaining
>
0
{
if
_
,
err
:=
io
.
CopyN
(
ioutil
.
Discard
,
c
.
br
,
c
.
readRemaining
);
err
!=
nil
{
return
noFrame
,
err
}
}
// 2. Read and parse first two bytes of frame header.
var
b
[
8
]
byte
if
err
:=
c
.
readFull
(
b
[
:
2
]);
err
!=
nil
{
return
noFrame
,
err
}
final
:=
b
[
0
]
&
finalBit
!=
0
frameType
:=
int
(
b
[
0
]
&
0xf
)
reserved
:=
int
((
b
[
0
]
>>
4
)
&
0x7
)
mask
:=
b
[
1
]
&
maskBit
!=
0
c
.
readRemaining
=
int64
(
b
[
1
]
&
0x7f
)
if
reserved
!=
0
{
return
noFrame
,
c
.
handleProtocolError
(
"unexpected reserved bits "
+
strconv
.
Itoa
(
reserved
))
}
switch
frameType
{
case
CloseMessage
,
PingMessage
,
PongMessage
:
if
c
.
readRemaining
>
maxControlFramePayloadSize
{
return
noFrame
,
c
.
handleProtocolError
(
"control frame length > 125"
)
}
if
!
final
{
return
noFrame
,
c
.
handleProtocolError
(
"control frame not final"
)
}
case
TextMessage
,
BinaryMessage
:
if
!
c
.
readFinal
{
return
noFrame
,
c
.
handleProtocolError
(
"message start before final message frame"
)
}
c
.
readFinal
=
final
case
continuationFrame
:
if
c
.
readFinal
{
return
noFrame
,
c
.
handleProtocolError
(
"continuation after final message frame"
)
}
c
.
readFinal
=
final
default
:
return
noFrame
,
c
.
handleProtocolError
(
"unknown opcode "
+
strconv
.
Itoa
(
frameType
))
}
// 3. Read and parse frame length.
switch
c
.
readRemaining
{
case
126
:
if
err
:=
c
.
readFull
(
b
[
:
2
]);
err
!=
nil
{
return
noFrame
,
err
}
c
.
readRemaining
=
int64
(
binary
.
BigEndian
.
Uint16
(
b
[
:
2
]))
case
127
:
if
err
:=
c
.
readFull
(
b
[
:
8
]);
err
!=
nil
{
return
noFrame
,
err
}
c
.
readRemaining
=
int64
(
binary
.
BigEndian
.
Uint64
(
b
[
:
8
]))
}
// 4. Handle frame masking.
if
mask
!=
c
.
isServer
{
return
noFrame
,
c
.
handleProtocolError
(
"incorrect mask flag"
)
}
if
mask
{
c
.
readMaskPos
=
0
if
err
:=
c
.
readFull
(
c
.
readMaskKey
[
:
]);
err
!=
nil
{
return
noFrame
,
err
}
}
// 5. For text and binary messages, enforce read limit and return.
if
frameType
==
continuationFrame
||
frameType
==
TextMessage
||
frameType
==
BinaryMessage
{
c
.
readLength
+=
c
.
readRemaining
if
c
.
readLimit
>
0
&&
c
.
readLength
>
c
.
readLimit
{
c
.
WriteControl
(
CloseMessage
,
FormatCloseMessage
(
CloseMessageTooBig
,
""
),
time
.
Now
()
.
Add
(
writeWait
))
return
noFrame
,
ErrReadLimit
}
return
frameType
,
nil
}
// 6. Read control frame payload.
var
payload
[]
byte
if
c
.
readRemaining
>
0
{
payload
=
make
([]
byte
,
c
.
readRemaining
)
c
.
readRemaining
=
0
if
err
:=
c
.
readFull
(
payload
);
err
!=
nil
{
return
noFrame
,
err
}
if
c
.
isServer
{
maskBytes
(
c
.
readMaskKey
,
0
,
payload
)
}
}
// 7. Process control frame payload.
switch
frameType
{
case
PongMessage
:
if
err
:=
c
.
handlePong
(
string
(
payload
));
err
!=
nil
{
return
noFrame
,
err
}
case
PingMessage
:
if
err
:=
c
.
handlePing
(
string
(
payload
));
err
!=
nil
{
return
noFrame
,
err
}
case
CloseMessage
:
c
.
WriteControl
(
CloseMessage
,
[]
byte
{},
time
.
Now
()
.
Add
(
writeWait
))
closeCode
:=
CloseNoStatusReceived
closeText
:=
""
if
len
(
payload
)
>=
2
{
closeCode
=
int
(
binary
.
BigEndian
.
Uint16
(
payload
))
closeText
=
string
(
payload
[
2
:
])
}
return
noFrame
,
&
CloseError
{
Code
:
closeCode
,
Text
:
closeText
}
}
return
frameType
,
nil
}
func
(
c
*
Conn
)
handleProtocolError
(
message
string
)
error
{
c
.
WriteControl
(
CloseMessage
,
FormatCloseMessage
(
CloseProtocolError
,
message
),
time
.
Now
()
.
Add
(
writeWait
))
return
errors
.
New
(
"websocket: "
+
message
)
}
// NextReader returns the next data message received from the peer. The
// returned messageType is either TextMessage or BinaryMessage.
//
// There can be at most one open reader on a connection. NextReader discards
// the previous message if the application has not already consumed it.
//
// The NextReader method and the readers returned from the method cannot be
// accessed by more than one goroutine at a time.
func
(
c
*
Conn
)
NextReader
()
(
messageType
int
,
r
io
.
Reader
,
err
error
)
{
c
.
readSeq
++
c
.
readLength
=
0
for
c
.
readErr
==
nil
{
frameType
,
err
:=
c
.
advanceFrame
()
if
err
!=
nil
{
c
.
readErr
=
hideTempErr
(
err
)
break
}
if
frameType
==
TextMessage
||
frameType
==
BinaryMessage
{
return
frameType
,
messageReader
{
c
,
c
.
readSeq
},
nil
}
}
return
noFrame
,
nil
,
c
.
readErr
}
type
messageReader
struct
{
c
*
Conn
seq
int
}
func
(
r
messageReader
)
Read
(
b
[]
byte
)
(
int
,
error
)
{
if
r
.
seq
!=
r
.
c
.
readSeq
{
return
0
,
io
.
EOF
}
for
r
.
c
.
readErr
==
nil
{
if
r
.
c
.
readRemaining
>
0
{
if
int64
(
len
(
b
))
>
r
.
c
.
readRemaining
{
b
=
b
[
:
r
.
c
.
readRemaining
]
}
n
,
err
:=
r
.
c
.
br
.
Read
(
b
)
r
.
c
.
readErr
=
hideTempErr
(
err
)
if
r
.
c
.
isServer
{
r
.
c
.
readMaskPos
=
maskBytes
(
r
.
c
.
readMaskKey
,
r
.
c
.
readMaskPos
,
b
[
:
n
])
}
r
.
c
.
readRemaining
-=
int64
(
n
)
return
n
,
r
.
c
.
readErr
}
if
r
.
c
.
readFinal
{
r
.
c
.
readSeq
++
return
0
,
io
.
EOF
}
frameType
,
err
:=
r
.
c
.
advanceFrame
()
switch
{
case
err
!=
nil
:
r
.
c
.
readErr
=
hideTempErr
(
err
)
case
frameType
==
TextMessage
||
frameType
==
BinaryMessage
:
r
.
c
.
readErr
=
errors
.
New
(
"websocket: internal error, unexpected text or binary in Reader"
)
}
}
err
:=
r
.
c
.
readErr
if
err
==
io
.
EOF
&&
r
.
seq
==
r
.
c
.
readSeq
{
err
=
errUnexpectedEOF
}
return
0
,
err
}
// ReadMessage is a helper method for getting a reader using NextReader and
// reading from that reader to a buffer.
func
(
c
*
Conn
)
ReadMessage
()
(
messageType
int
,
p
[]
byte
,
err
error
)
{
var
r
io
.
Reader
messageType
,
r
,
err
=
c
.
NextReader
()
if
err
!=
nil
{
return
messageType
,
nil
,
err
}
p
,
err
=
ioutil
.
ReadAll
(
r
)
return
messageType
,
p
,
err
}
// SetReadDeadline sets the read deadline on the underlying network connection.
// After a read has timed out, the websocket connection state is corrupt and
// all future reads will return an error. A zero value for t means reads will
// not time out.
func
(
c
*
Conn
)
SetReadDeadline
(
t
time
.
Time
)
error
{
return
c
.
conn
.
SetReadDeadline
(
t
)
}
// SetReadLimit sets the maximum size for a message read from the peer. If a
// message exceeds the limit, the connection sends a close frame to the peer
// and returns ErrReadLimit to the application.
func
(
c
*
Conn
)
SetReadLimit
(
limit
int64
)
{
c
.
readLimit
=
limit
}
// SetPingHandler sets the handler for ping messages received from the peer.
// The appData argument to h is the PING frame application data. The default
// ping handler sends a pong to the peer.
func
(
c
*
Conn
)
SetPingHandler
(
h
func
(
appData
string
)
error
)
{
if
h
==
nil
{
h
=
func
(
message
string
)
error
{
err
:=
c
.
WriteControl
(
PongMessage
,
[]
byte
(
message
),
time
.
Now
()
.
Add
(
writeWait
))
if
err
==
ErrCloseSent
{
return
nil
}
else
if
e
,
ok
:=
err
.
(
net
.
Error
);
ok
&&
e
.
Temporary
()
{
return
nil
}
return
err
}
}
c
.
handlePing
=
h
}
// SetPongHandler sets the handler for pong messages received from the peer.
// The appData argument to h is the PONG frame application data. The default
// pong handler does nothing.
func
(
c
*
Conn
)
SetPongHandler
(
h
func
(
appData
string
)
error
)
{
if
h
==
nil
{
h
=
func
(
string
)
error
{
return
nil
}
}
c
.
handlePong
=
h
}
// UnderlyingConn returns the internal net.Conn. This can be used to further
// modifications to connection specific flags.
func
(
c
*
Conn
)
UnderlyingConn
()
net
.
Conn
{
return
c
.
conn
}
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
func
FormatCloseMessage
(
closeCode
int
,
text
string
)
[]
byte
{
buf
:=
make
([]
byte
,
2
+
len
(
text
))
binary
.
BigEndian
.
PutUint16
(
buf
,
uint16
(
closeCode
))
copy
(
buf
[
2
:
],
text
)
return
buf
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/conn_test.go
0 → 100644
View file @
8acc21e8
// 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"
"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
nil
}
func
(
c
fakeNetConn
)
RemoteAddr
()
net
.
Addr
{
return
nil
}
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
}
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
)
}
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
)
for
_
,
n
:=
range
frameSizes
{
for
_
,
iocopy
:=
range
[]
bool
{
true
,
false
}
{
name
:=
fmt
.
Sprintf
(
"s:%v, r:%s, n:%d c:%v"
,
isServer
,
chunker
.
name
,
n
,
iocopy
)
w
,
err
:=
wc
.
NextWriter
(
TextMessage
)
if
err
!=
nil
{
t
.
Errorf
(
"%s: wc.NextWriter() returned %v"
,
name
,
err
)
continue
}
var
nn
int
if
iocopy
{
var
n64
int64
n64
,
err
=
io
.
Copy
(
w
,
bytes
.
NewReader
(
writeBuf
[
:
n
]))
nn
=
int
(
n64
)
}
else
{
nn
,
err
=
w
.
Write
(
writeBuf
[
:
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
TestCloseBeforeFinalFrame
(
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
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
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
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
))
}
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/doc.go
0 → 100644
View file @
8acc21e8
// 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 implements the WebSocket protocol defined in RFC 6455.
//
// Overview
//
// The Conn type represents a WebSocket connection. A server application uses
// the Upgrade function from an Upgrader object with a HTTP request handler
// to get a pointer to a Conn:
//
// var upgrader = websocket.Upgrader{
// ReadBufferSize: 1024,
// WriteBufferSize: 1024,
// }
//
// func handler(w http.ResponseWriter, r *http.Request) {
// conn, err := upgrader.Upgrade(w, r, nil)
// if err != nil {
// log.Println(err)
// return
// }
// ... Use conn to send and receive messages.
// }
//
// Call the connection's WriteMessage and ReadMessage methods to send and
// receive messages as a slice of bytes. This snippet of code shows how to echo
// messages using these methods:
//
// for {
// messageType, p, err := conn.ReadMessage()
// if err != nil {
// return
// }
// if err = conn.WriteMessage(messageType, p); err != nil {
// return err
// }
// }
//
// In above snippet of code, p is a []byte and messageType is an int with value
// websocket.BinaryMessage or websocket.TextMessage.
//
// An application can also send and receive messages using the io.WriteCloser
// and io.Reader interfaces. To send a message, call the connection NextWriter
// method to get an io.WriteCloser, write the message to the writer and close
// the writer when done. To receive a message, call the connection NextReader
// method to get an io.Reader and read until io.EOF is returned. This snippet
// snippet shows how to echo messages using the NextWriter and NextReader
// methods:
//
// for {
// messageType, r, err := conn.NextReader()
// if err != nil {
// return
// }
// w, err := conn.NextWriter(messageType)
// if err != nil {
// return err
// }
// if _, err := io.Copy(w, r); err != nil {
// return err
// }
// if err := w.Close(); err != nil {
// return err
// }
// }
//
// Data Messages
//
// The WebSocket protocol distinguishes between text and binary data messages.
// Text messages are interpreted as UTF-8 encoded text. The interpretation of
// binary messages is left to the application.
//
// This package uses the TextMessage and BinaryMessage integer constants to
// identify the two data message types. The ReadMessage and NextReader methods
// return the type of the received message. The messageType argument to the
// WriteMessage and NextWriter methods specifies the type of a sent message.
//
// It is the application's responsibility to ensure that text messages are
// valid UTF-8 encoded text.
//
// Control Messages
//
// The WebSocket protocol defines three types of control messages: close, ping
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
// methods to send a control message to the peer.
//
// Connections handle received ping and pong messages by invoking a callback
// function set with SetPingHandler and SetPongHandler methods. These callback
// functions can be invoked from the ReadMessage method, the NextReader method
// or from a call to the data message reader returned from NextReader.
//
// Connections handle received close messages by returning an error from the
// ReadMessage method, the NextReader method or from a call to the data message
// reader returned from NextReader.
//
// Concurrency
//
// Connections support one concurrent reader and one concurrent writer.
//
// Applications are responsible for ensuring that no more than one goroutine
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
// WriteJSON) concurrently and that no more than one goroutine calls the read
// methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler,
// SetPingHandler) concurrently.
//
// The Close and WriteControl methods can be called concurrently with all other
// methods.
//
// Read is Required
//
// The application must read the connection to process ping and close messages
// sent from the peer. If the application is not otherwise interested in
// messages from the peer, then the application should start a goroutine to read
// and discard messages from the peer. A simple example is:
//
// func readLoop(c *websocket.Conn) {
// for {
// if _, _, err := c.NextReader(); err != nil {
// c.Close()
// break
// }
// }
// }
//
// Origin Considerations
//
// Web browsers allow Javascript applications to open a WebSocket connection to
// any host. It's up to the server to enforce an origin policy using the Origin
// request header sent by the browser.
//
// The Upgrader calls the function specified in the CheckOrigin field to check
// the origin. If the CheckOrigin function returns false, then the Upgrade
// method fails the WebSocket handshake with HTTP status 403.
//
// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
// the handshake if the Origin request header is present and not equal to the
// Host request header.
//
// An application can allow connections from any origin by specifying a
// function that always returns true:
//
// var upgrader = websocket.Upgrader{
// CheckOrigin: func(r *http.Request) bool { return true },
// }
//
// The deprecated Upgrade function does not enforce an origin policy. It's the
// application's responsibility to check the Origin header before calling
// Upgrade.
package
websocket
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/autobahn/README.md
0 → 100644
View file @
8acc21e8
# 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.
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/autobahn/fuzzingclient.json
0 → 100644
View file @
8acc21e8
{
"options"
:
{
"failByDrop"
:
false
},
"outdir"
:
"./reports/clients"
,
"servers"
:
[
{
"agent"
:
"ReadAllWriteMessage"
,
"url"
:
"ws://localhost:9000/m"
,
"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"
:
{}
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/autobahn/server.go
0 → 100644
View file @
8acc21e8
// 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
(
"QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket"
"errors"
"flag"
"io"
"log"
"net/http"
"time"
"unicode/utf8"
)
var
upgrader
=
websocket
.
Upgrader
{
ReadBufferSize
:
4096
,
WriteBufferSize
:
4096
,
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
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
{
err
=
conn
.
WriteMessage
(
mt
,
b
)
if
err
!=
nil
{
log
.
Println
(
"WriteMessage:"
,
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
)
}
func
echoReadAllWriteMessage
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
echoReadAll
(
w
,
r
,
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
)
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
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/chat/README.md
0 → 100644
View file @
8acc21e8
# Chat Example
This application shows how to use use the
[
websocket
](
https://github.com/gorilla/websocket
)
package and
[
jQuery
](
http://jquery.com
)
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.
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/chat/conn.go
0 → 100644
View file @
8acc21e8
// 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
(
"QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket"
"log"
"net/http"
"time"
)
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
upgrader
=
websocket
.
Upgrader
{
ReadBufferSize
:
1024
,
WriteBufferSize
:
1024
,
}
// connection is an middleman between the websocket connection and the hub.
type
connection
struct
{
// The websocket connection.
ws
*
websocket
.
Conn
// Buffered channel of outbound messages.
send
chan
[]
byte
}
// readPump pumps messages from the websocket connection to the hub.
func
(
c
*
connection
)
readPump
()
{
defer
func
()
{
h
.
unregister
<-
c
c
.
ws
.
Close
()
}()
c
.
ws
.
SetReadLimit
(
maxMessageSize
)
c
.
ws
.
SetReadDeadline
(
time
.
Now
()
.
Add
(
pongWait
))
c
.
ws
.
SetPongHandler
(
func
(
string
)
error
{
c
.
ws
.
SetReadDeadline
(
time
.
Now
()
.
Add
(
pongWait
));
return
nil
})
for
{
_
,
message
,
err
:=
c
.
ws
.
ReadMessage
()
if
err
!=
nil
{
break
}
h
.
broadcast
<-
message
}
}
// write writes a message with the given message type and payload.
func
(
c
*
connection
)
write
(
mt
int
,
payload
[]
byte
)
error
{
c
.
ws
.
SetWriteDeadline
(
time
.
Now
()
.
Add
(
writeWait
))
return
c
.
ws
.
WriteMessage
(
mt
,
payload
)
}
// writePump pumps messages from the hub to the websocket connection.
func
(
c
*
connection
)
writePump
()
{
ticker
:=
time
.
NewTicker
(
pingPeriod
)
defer
func
()
{
ticker
.
Stop
()
c
.
ws
.
Close
()
}()
for
{
select
{
case
message
,
ok
:=
<-
c
.
send
:
if
!
ok
{
c
.
write
(
websocket
.
CloseMessage
,
[]
byte
{})
return
}
if
err
:=
c
.
write
(
websocket
.
TextMessage
,
message
);
err
!=
nil
{
return
}
case
<-
ticker
.
C
:
if
err
:=
c
.
write
(
websocket
.
PingMessage
,
[]
byte
{});
err
!=
nil
{
return
}
}
}
}
// serveWs handles websocket requests from the peer.
func
serveWs
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
ws
,
err
:=
upgrader
.
Upgrade
(
w
,
r
,
nil
)
if
err
!=
nil
{
log
.
Println
(
err
)
return
}
c
:=
&
connection
{
send
:
make
(
chan
[]
byte
,
256
),
ws
:
ws
}
h
.
register
<-
c
go
c
.
writePump
()
c
.
readPump
()
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/chat/home.html
0 → 100644
View file @
8acc21e8
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<title>
Chat Example
</title>
<script
src=
"//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"
></script>
<script
type=
"text/javascript"
>
$
(
function
()
{
var
conn
;
var
msg
=
$
(
"
#msg
"
);
var
log
=
$
(
"
#log
"
);
function
appendLog
(
msg
)
{
var
d
=
log
[
0
]
var
doScroll
=
d
.
scrollTop
==
d
.
scrollHeight
-
d
.
clientHeight
;
msg
.
appendTo
(
log
)
if
(
doScroll
)
{
d
.
scrollTop
=
d
.
scrollHeight
-
d
.
clientHeight
;
}
}
$
(
"
#form
"
).
submit
(
function
()
{
if
(
!
conn
)
{
return
false
;
}
if
(
!
msg
.
val
())
{
return
false
;
}
conn
.
send
(
msg
.
val
());
msg
.
val
(
""
);
return
false
});
if
(
window
[
"
WebSocket
"
])
{
conn
=
new
WebSocket
(
"
ws://{{$}}/ws
"
);
conn
.
onclose
=
function
(
evt
)
{
appendLog
(
$
(
"
<div><b>Connection closed.</b></div>
"
))
}
conn
.
onmessage
=
function
(
evt
)
{
appendLog
(
$
(
"
<div/>
"
).
text
(
evt
.
data
))
}
}
else
{
appendLog
(
$
(
"
<div><b>Your browser does not support WebSockets.</b></div>
"
))
}
});
</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>
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/chat/hub.go
0 → 100644
View file @
8acc21e8
// 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 connections and broadcasts messages to the
// connections.
type
hub
struct
{
// Registered connections.
connections
map
[
*
connection
]
bool
// Inbound messages from the connections.
broadcast
chan
[]
byte
// Register requests from the connections.
register
chan
*
connection
// Unregister requests from connections.
unregister
chan
*
connection
}
var
h
=
hub
{
broadcast
:
make
(
chan
[]
byte
),
register
:
make
(
chan
*
connection
),
unregister
:
make
(
chan
*
connection
),
connections
:
make
(
map
[
*
connection
]
bool
),
}
func
(
h
*
hub
)
run
()
{
for
{
select
{
case
c
:=
<-
h
.
register
:
h
.
connections
[
c
]
=
true
case
c
:=
<-
h
.
unregister
:
if
_
,
ok
:=
h
.
connections
[
c
];
ok
{
delete
(
h
.
connections
,
c
)
close
(
c
.
send
)
}
case
m
:=
<-
h
.
broadcast
:
for
c
:=
range
h
.
connections
{
select
{
case
c
.
send
<-
m
:
default
:
close
(
c
.
send
)
delete
(
h
.
connections
,
c
)
}
}
}
}
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/chat/main.go
0 → 100644
View file @
8acc21e8
// 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"
"text/template"
)
var
addr
=
flag
.
String
(
"addr"
,
":8080"
,
"http service address"
)
var
homeTempl
=
template
.
Must
(
template
.
ParseFiles
(
"home.html"
))
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"
)
homeTempl
.
Execute
(
w
,
r
.
Host
)
}
func
main
()
{
flag
.
Parse
()
go
h
.
run
()
http
.
HandleFunc
(
"/"
,
serveHome
)
http
.
HandleFunc
(
"/ws"
,
serveWs
)
err
:=
http
.
ListenAndServe
(
*
addr
,
nil
)
if
err
!=
nil
{
log
.
Fatal
(
"ListenAndServe: "
,
err
)
}
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/command/README.md
0 → 100644
View file @
8acc21e8
# 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
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
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/command/home.html
0 → 100644
View file @
8acc21e8
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<title>
Command Example
</title>
<script
src=
"//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"
></script>
<script
type=
"text/javascript"
>
$
(
function
()
{
var
conn
;
var
msg
=
$
(
"
#msg
"
);
var
log
=
$
(
"
#log
"
);
function
appendLog
(
msg
)
{
var
d
=
log
[
0
]
var
doScroll
=
d
.
scrollTop
==
d
.
scrollHeight
-
d
.
clientHeight
;
msg
.
appendTo
(
log
)
if
(
doScroll
)
{
d
.
scrollTop
=
d
.
scrollHeight
-
d
.
clientHeight
;
}
}
$
(
"
#form
"
).
submit
(
function
()
{
if
(
!
conn
)
{
return
false
;
}
if
(
!
msg
.
val
())
{
return
false
;
}
conn
.
send
(
msg
.
val
());
msg
.
val
(
""
);
return
false
});
if
(
window
[
"
WebSocket
"
])
{
conn
=
new
WebSocket
(
"
ws://{{$}}/ws
"
);
conn
.
onclose
=
function
(
evt
)
{
appendLog
(
$
(
"
<div><b>Connection closed.</b></div>
"
))
}
conn
.
onmessage
=
function
(
evt
)
{
appendLog
(
$
(
"
<pre/>
"
).
text
(
evt
.
data
))
}
}
else
{
appendLog
(
$
(
"
<div><b>Your browser does not support WebSockets.</b></div>
"
))
}
});
</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>
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/command/main.go
0 → 100644
View file @
8acc21e8
// 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"
"text/template"
"time"
"QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket"
)
var
(
addr
=
flag
.
String
(
"addr"
,
"127.0.0.1:8080"
,
"http service address"
)
cmdPath
string
homeTempl
=
template
.
Must
(
template
.
ParseFiles
(
"home.html"
))
)
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
)
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
()
{
ws
.
Close
()
close
(
done
)
}()
s
:=
bufio
.
NewScanner
(
r
)
for
s
.
Scan
()
{
ws
.
SetWriteDeadline
(
time
.
Now
()
.
Add
(
writeWait
))
if
err
:=
ws
.
WriteMessage
(
websocket
.
TextMessage
,
s
.
Bytes
());
err
!=
nil
{
break
}
}
if
s
.
Err
()
!=
nil
{
log
.
Println
(
"scan:"
,
s
.
Err
())
}
}
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
}
w
.
Header
()
.
Set
(
"Content-Type"
,
"text/html; charset=utf-8"
)
homeTempl
.
Execute
(
w
,
r
.
Host
)
}
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
))
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/echo/README.md
0 → 100644
View file @
8acc21e8
# 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.
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/echo/client.go
0 → 100644
View file @
8acc21e8
// 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"
"time"
"github.com/gorilla/websocket"
)
var
addr
=
flag
.
String
(
"addr"
,
"localhost:8080"
,
"http service address"
)
func
main
()
{
flag
.
Parse
()
log
.
SetFlags
(
0
)
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
()
go
func
()
{
defer
c
.
Close
()
for
{
_
,
message
,
err
:=
c
.
ReadMessage
()
if
err
!=
nil
{
log
.
Println
(
"read:"
,
err
)
break
}
log
.
Printf
(
"recv: %s"
,
message
)
}
}()
ticker
:=
time
.
NewTicker
(
time
.
Second
)
defer
ticker
.
Stop
()
for
t
:=
range
ticker
.
C
{
err
:=
c
.
WriteMessage
(
websocket
.
TextMessage
,
[]
byte
(
t
.
String
()))
if
err
!=
nil
{
log
.
Println
(
"write:"
,
err
)
break
}
}
}
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/echo/server.go
0 → 100644
View file @
8acc21e8
// 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>
`
))
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/filewatch/README.md
0 → 100644
View file @
8acc21e8
# 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.
vendor/QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/websocket/examples/filewatch/main.go
0 → 100644
View file @
8acc21e8
// 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"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"text/template"
"time"
"QmNvACkuNdmJwK4SBHLrxDjEerWqSFnd2qy7CKcn4ouZ3p/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>
`
Prev
1
2
3
4
5
6
…
9
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment