mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Add one to one demo. 4.0.96
This commit is contained in:
parent
f7473c90bc
commit
5a611e0c4b
41 changed files with 9083 additions and 9 deletions
24
README.md
24
README.md
|
@ -80,6 +80,25 @@ Other important wiki:
|
||||||
* Usage: How to improve edge performance for multiple CPUs? ([CN][v4_CN_REUSEPORT], [EN][v4_EN_REUSEPORT])
|
* Usage: How to improve edge performance for multiple CPUs? ([CN][v4_CN_REUSEPORT], [EN][v4_EN_REUSEPORT])
|
||||||
* Usage: How to file a bug or contact us? ([CN][v4_CN_Contact], [EN][v4_EN_Contact])
|
* Usage: How to file a bug or contact us? ([CN][v4_CN_Contact], [EN][v4_EN_Contact])
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
The ports used by SRS:
|
||||||
|
|
||||||
|
* tcp://1935, for RTMP live streaming server.
|
||||||
|
* tcp://1985, HTTP API server.
|
||||||
|
* tcp://1990, HTTPS API server.
|
||||||
|
* tcp://8080, HTTP live streaming server.
|
||||||
|
* tcp://8088, HTTPS live streaming server.
|
||||||
|
* udp://8000, [WebRTC Media](https://github.com/ossrs/srs/wiki/v4_CN_WebRTC) server.
|
||||||
|
* udp://1980, [WebRTC Signaling](https://github.com/ossrs/signaling#usage) server.
|
||||||
|
* udp://8935, Stream Caster: [Push MPEGTS over UDP](https://github.com/ossrs/srs/wiki/v4_CN_Streamer#push-mpeg-ts-over-udp) server.
|
||||||
|
* tcp://554, Stream Caster: [Push RTSP](https://github.com/ossrs/srs/wiki/v4_CN_Streamer#push-rtsp-to-srs) server.
|
||||||
|
* tcp://8936, Stream Caster: [Push HTTP-FLV](https://github.com/ossrs/srs/wiki/v4_CN_Streamer#push-http-flv-to-srs) server.
|
||||||
|
* tcp://5060, Stream Caster: [Push GB28181 SIP](https://github.com/ossrs/srs/issues/1500#issuecomment-606695679) server.
|
||||||
|
* udp://9000, Stream Caster: [Push GB28181 Media(bundle)](https://github.com/ossrs/srs/issues/1500#issuecomment-606695679) server.
|
||||||
|
* udp://58200-58300, Stream Caster: [Push GB28181 Media(no-bundle)](https://github.com/ossrs/srs/issues/1500#issuecomment-606695679) server.
|
||||||
|
* udp://10080, Stream Caster: [Push SRT Media](https://github.com/ossrs/srs/issues/1147#issuecomment-577469119) server.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- [x] Using coroutine by ST, it's really simple and stupid enough.
|
- [x] Using coroutine by ST, it's really simple and stupid enough.
|
||||||
|
@ -157,8 +176,9 @@ Other important wiki:
|
||||||
|
|
||||||
## V4 changes
|
## V4 changes
|
||||||
|
|
||||||
* v5.0, 2021-04-20, Support RTC2RTMP bridger and shared FastTimer. 4.0.95
|
* v4.0, 2021-05-02, Add one to one demo. 4.0.96
|
||||||
* v5.0, 2021-04-20, Refine transcoder to support aac2opus and opus2aac. 4.0.94
|
* v4.0, 2021-04-20, Support RTC2RTMP bridger and shared FastTimer. 4.0.95
|
||||||
|
* v4.0, 2021-04-20, Refine transcoder to support aac2opus and opus2aac. 4.0.94
|
||||||
* v4.0, 2021-05-01, Timer: Extract and apply shared FastTimer. 4.0.93
|
* v4.0, 2021-05-01, Timer: Extract and apply shared FastTimer. 4.0.93
|
||||||
* v4.0, 2021-04-29, RTC: Support AV1 for Chrome M90. 4.0.91
|
* v4.0, 2021-04-29, RTC: Support AV1 for Chrome M90. 4.0.91
|
||||||
* v4.0, 2021-04-24, Change push-RTSP as deprecated feature.
|
* v4.0, 2021-04-24, Change push-RTSP as deprecated feature.
|
||||||
|
|
18
trunk/3rdparty/signaling/.gitignore
vendored
Normal file
18
trunk/3rdparty/signaling/.gitignore
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
.format.txt
|
||||||
|
objs
|
21
trunk/3rdparty/signaling/LICENSE
vendored
Normal file
21
trunk/3rdparty/signaling/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 srs-org
|
||||||
|
|
||||||
|
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.
|
19
trunk/3rdparty/signaling/Makefile
vendored
Normal file
19
trunk/3rdparty/signaling/Makefile
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
.PHONY: help default clean signaling
|
||||||
|
|
||||||
|
default: signaling
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f ./objs/signaling
|
||||||
|
|
||||||
|
.format.txt: *.go
|
||||||
|
gofmt -w .
|
||||||
|
echo "done" > .format.txt
|
||||||
|
|
||||||
|
signaling: ./objs/signaling
|
||||||
|
|
||||||
|
./objs/signaling: .format.txt *.go Makefile
|
||||||
|
go build -mod=vendor -o objs/signaling .
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Usage: make [signaling]"
|
||||||
|
@echo " signaling Make the signaling to ./objs/signaling"
|
24
trunk/3rdparty/signaling/README.md
vendored
Normal file
24
trunk/3rdparty/signaling/README.md
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# signaling
|
||||||
|
|
||||||
|
WebRTC signaling for https://github.com/ossrs/srs
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Build and [run SRS](https://github.com/ossrs/srs/tree/4.0release#usage):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone -b 4.0release https://gitee.com/ossrs/srs.git srs &&
|
||||||
|
cd srs/trunk && ./configure && make && ./objs/srs -c conf/rtc.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Build and run signaling:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make && ./objs/signaling
|
||||||
|
```
|
||||||
|
|
||||||
|
Open the H5 demos:
|
||||||
|
|
||||||
|
* [WebRTC: One to One over SFU(SRS)](http://localhost:1989/demos/one2one.html?autostart=true)
|
||||||
|
|
||||||
|
Winlin 2021.05
|
8
trunk/3rdparty/signaling/go.mod
vendored
Normal file
8
trunk/3rdparty/signaling/go.mod
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module github.com/ossrs/signaling
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/ossrs/go-oryx-lib v0.0.8
|
||||||
|
golang.org/x/net v0.0.0-20210502030024-e5908800b52b
|
||||||
|
)
|
9
trunk/3rdparty/signaling/go.sum
vendored
Normal file
9
trunk/3rdparty/signaling/go.sum
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
github.com/ossrs/go-oryx-lib v0.0.8 h1:k8ml3ZLsjIMoQEdZdWuy8zkU0w/fbJSyHvT/s9NyeCc=
|
||||||
|
github.com/ossrs/go-oryx-lib v0.0.8/go.mod h1:i2tH4TZBzAw5h+HwGrNOKvP/nmZgSQz0OEnLLdzcT/8=
|
||||||
|
golang.org/x/net v0.0.0-20210502030024-e5908800b52b h1:jCRjgm6WJHzM8VQrm/es2wXYqqbq0NZ1yXFHHgzkiVQ=
|
||||||
|
golang.org/x/net v0.0.0-20210502030024-e5908800b52b/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
319
trunk/3rdparty/signaling/main.go
vendored
Normal file
319
trunk/3rdparty/signaling/main.go
vendored
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Winlin
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ossrs/go-oryx-lib/errors"
|
||||||
|
"github.com/ossrs/go-oryx-lib/logger"
|
||||||
|
"golang.org/x/net/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Participant struct {
|
||||||
|
Room *Room `json:"-"`
|
||||||
|
Display string `json:"display"`
|
||||||
|
Publishing bool `json:"publishing"`
|
||||||
|
Out chan []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Participant) String() string {
|
||||||
|
return fmt.Sprintf("display=%v, room=%v", v.Display, v.Room.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Room struct {
|
||||||
|
Name string `json:"room"`
|
||||||
|
Participants []*Participant `json:"participants"`
|
||||||
|
lock sync.RWMutex `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Room) String() string {
|
||||||
|
return fmt.Sprintf("room=%v, participants=%v", v.Name, len(v.Participants))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Room) Add(p *Participant) error {
|
||||||
|
v.lock.Lock()
|
||||||
|
defer v.lock.Unlock()
|
||||||
|
|
||||||
|
for _, r := range v.Participants {
|
||||||
|
if r.Display == p.Display {
|
||||||
|
return errors.Errorf("Participant %v exists in room %v", p.Display, v.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Participants = append(v.Participants, p)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Room) Get(display string) *Participant {
|
||||||
|
v.lock.RLock()
|
||||||
|
defer v.lock.RUnlock()
|
||||||
|
|
||||||
|
for _, r := range v.Participants {
|
||||||
|
if r.Display == display {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Room) Remove(p *Participant) {
|
||||||
|
v.lock.Lock()
|
||||||
|
defer v.lock.Unlock()
|
||||||
|
|
||||||
|
for i, r := range v.Participants {
|
||||||
|
if p == r {
|
||||||
|
v.Participants = append(v.Participants[:i], v.Participants[i+1:]...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Room) Notify(ctx context.Context, peer *Participant, event string) {
|
||||||
|
var participants []*Participant
|
||||||
|
func() {
|
||||||
|
v.lock.RLock()
|
||||||
|
defer v.lock.RUnlock()
|
||||||
|
participants = append(participants, v.Participants...)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for _, r := range participants {
|
||||||
|
if r == peer {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
res := struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
Event string `json:"event"`
|
||||||
|
Room string `json:"room"`
|
||||||
|
Self *Participant `json:"self"`
|
||||||
|
Peer *Participant `json:"peer"`
|
||||||
|
Participants []*Participant `json:"participants"`
|
||||||
|
}{
|
||||||
|
"notify", event, v.Name, r, peer, participants,
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(struct {
|
||||||
|
Message interface{} `json:"msg"`
|
||||||
|
}{
|
||||||
|
res,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case r.Out <- b:
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Tf(ctx, "Notify %v about %v %v", r, peer, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var listen string
|
||||||
|
flag.StringVar(&listen, "listen", "1989", "The TCP listen port")
|
||||||
|
|
||||||
|
var html string
|
||||||
|
flag.StringVar(&html, "root", "./www", "The www web root")
|
||||||
|
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Println(fmt.Sprintf("Usage: %v [Options]", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("Options:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -listen The TCP listen port. Default: %v", listen))
|
||||||
|
fmt.Println(fmt.Sprintf(" -root The www web root. Default: %v", html))
|
||||||
|
fmt.Println(fmt.Sprintf("For example:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -listen %v -html %v", os.Args[0], listen, html))
|
||||||
|
}
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if !strings.Contains(listen, ":") {
|
||||||
|
listen = ":" + listen
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
home := listen
|
||||||
|
if strings.HasPrefix(home, ":") {
|
||||||
|
home = "http://localhost" + listen
|
||||||
|
}
|
||||||
|
|
||||||
|
if !path.IsAbs(html) && path.IsAbs(os.Args[0]) {
|
||||||
|
html = path.Join(path.Dir(os.Args[0]), html)
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "Signaling ok, root=%v, home page is %v", html, home)
|
||||||
|
|
||||||
|
http.Handle("/", http.FileServer(http.Dir(html)))
|
||||||
|
|
||||||
|
// Key is name of room, value is Room
|
||||||
|
var rooms sync.Map
|
||||||
|
http.Handle("/sig/v1/rtc", websocket.Handler(func(c *websocket.Conn) {
|
||||||
|
ctx, cancel := context.WithCancel(logger.WithContext(ctx))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
r := c.Request()
|
||||||
|
logger.Tf(ctx, "Serve client %v at %v", r.RemoteAddr, r.RequestURI)
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
var self *Participant
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
if self != nil {
|
||||||
|
self.Room.Remove(self)
|
||||||
|
logger.Tf(ctx, "Remove client %v", self)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
inMessages := make(chan []byte, 0)
|
||||||
|
go func() {
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
buf := make([]byte, 16384)
|
||||||
|
for {
|
||||||
|
n, err := c.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
logger.Wf(ctx, "Ignore err %v for %v", err, r.RemoteAddr)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case inMessages <- buf[:n]:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
outMessages := make(chan []byte, 0)
|
||||||
|
go func() {
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
handleMessage := func(m []byte) error {
|
||||||
|
action := struct {
|
||||||
|
TID string `json:"tid"`
|
||||||
|
Message struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
} `json:"msg"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(m, &action); err != nil {
|
||||||
|
return errors.Wrapf(err, "Unmarshal %s", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var res interface{}
|
||||||
|
var p *Participant
|
||||||
|
if action.Message.Action == "join" {
|
||||||
|
obj := struct {
|
||||||
|
Message struct {
|
||||||
|
Room string `json:"room"`
|
||||||
|
Display string `json:"display"`
|
||||||
|
} `json:"msg"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(m, &obj); err != nil {
|
||||||
|
return errors.Wrapf(err, "Unmarshal %s", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _ := rooms.LoadOrStore(obj.Message.Room, &Room{Name: obj.Message.Room})
|
||||||
|
p = &Participant{Room: r.(*Room), Display: obj.Message.Display, Out: outMessages}
|
||||||
|
if err := r.(*Room).Add(p); err != nil {
|
||||||
|
return errors.Wrapf(err, "join")
|
||||||
|
}
|
||||||
|
|
||||||
|
self = p
|
||||||
|
logger.Tf(ctx, "Join %v ok", self)
|
||||||
|
|
||||||
|
res = struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
Room string `json:"room"`
|
||||||
|
Self *Participant `json:"self"`
|
||||||
|
Participants []*Participant `json:"participants"`
|
||||||
|
}{
|
||||||
|
action.Message.Action, obj.Message.Room, p, r.(*Room).Participants,
|
||||||
|
}
|
||||||
|
|
||||||
|
go r.(*Room).Notify(ctx, p, action.Message.Action)
|
||||||
|
} else if action.Message.Action == "publish" {
|
||||||
|
obj := struct {
|
||||||
|
Message struct {
|
||||||
|
Room string `json:"room"`
|
||||||
|
Display string `json:"display"`
|
||||||
|
} `json:"msg"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(m, &obj); err != nil {
|
||||||
|
return errors.Wrapf(err, "Unmarshal %s", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _ := rooms.LoadOrStore(obj.Message.Room, &Room{Name: obj.Message.Room})
|
||||||
|
p := r.(*Room).Get(obj.Message.Display)
|
||||||
|
|
||||||
|
// Now, the peer is publishing.
|
||||||
|
p.Publishing = true
|
||||||
|
|
||||||
|
go r.(*Room).Notify(ctx, p, action.Message.Action)
|
||||||
|
} else {
|
||||||
|
return errors.Errorf("Invalid message %s", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, err := json.Marshal(struct {
|
||||||
|
TID string `json:"tid"`
|
||||||
|
Message interface{} `json:"msg"`
|
||||||
|
}{
|
||||||
|
action.TID, res,
|
||||||
|
}); err != nil {
|
||||||
|
return errors.Wrapf(err, "marshal")
|
||||||
|
} else {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case outMessages <- b:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for m := range inMessages {
|
||||||
|
if err := handleMessage(m); err != nil {
|
||||||
|
logger.Wf(ctx, "Handle %s err %v", m, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for m := range outMessages {
|
||||||
|
if _, err := c.Write(m); err != nil {
|
||||||
|
logger.Wf(ctx, "Ignore err %v for %v", err, r.RemoteAddr)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
http.ListenAndServe(listen, nil)
|
||||||
|
}
|
21
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/LICENSE
generated
vendored
Normal file
21
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2017 winlin
|
||||||
|
|
||||||
|
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.
|
23
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/errors/LICENSE
generated
vendored
Normal file
23
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/errors/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
52
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/errors/README.md
generated
vendored
Normal file
52
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/errors/README.md
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors)
|
||||||
|
|
||||||
|
Package errors provides simple error handling primitives.
|
||||||
|
|
||||||
|
`go get github.com/pkg/errors`
|
||||||
|
|
||||||
|
The traditional error handling idiom in Go is roughly akin to
|
||||||
|
```go
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
|
||||||
|
|
||||||
|
## Adding context to an error
|
||||||
|
|
||||||
|
The errors.Wrap function returns a new error that adds context to the original error. For example
|
||||||
|
```go
|
||||||
|
_, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "read failed")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Retrieving the cause of an error
|
||||||
|
|
||||||
|
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
|
||||||
|
```go
|
||||||
|
type causer interface {
|
||||||
|
Cause() error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
|
||||||
|
```go
|
||||||
|
switch err := errors.Cause(err).(type) {
|
||||||
|
case *MyError:
|
||||||
|
// handle specifically
|
||||||
|
default:
|
||||||
|
// unknown error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
|
||||||
|
|
||||||
|
Before proposing a change, please discuss your change by raising an issue.
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
BSD-2-Clause
|
270
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/errors/errors.go
generated
vendored
Normal file
270
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/errors/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
// Package errors provides simple error handling primitives.
|
||||||
|
//
|
||||||
|
// The traditional error handling idiom in Go is roughly akin to
|
||||||
|
//
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// which applied recursively up the call stack results in error reports
|
||||||
|
// without context or debugging information. The errors package allows
|
||||||
|
// programmers to add context to the failure path in their code in a way
|
||||||
|
// that does not destroy the original value of the error.
|
||||||
|
//
|
||||||
|
// Adding context to an error
|
||||||
|
//
|
||||||
|
// The errors.Wrap function returns a new error that adds context to the
|
||||||
|
// original error by recording a stack trace at the point Wrap is called,
|
||||||
|
// and the supplied message. For example
|
||||||
|
//
|
||||||
|
// _, err := ioutil.ReadAll(r)
|
||||||
|
// if err != nil {
|
||||||
|
// return errors.Wrap(err, "read failed")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If additional control is required the errors.WithStack and errors.WithMessage
|
||||||
|
// functions destructure errors.Wrap into its component operations of annotating
|
||||||
|
// an error with a stack trace and an a message, respectively.
|
||||||
|
//
|
||||||
|
// Retrieving the cause of an error
|
||||||
|
//
|
||||||
|
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||||
|
// preceding error. Depending on the nature of the error it may be necessary
|
||||||
|
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||||
|
// for inspection. Any error value which implements this interface
|
||||||
|
//
|
||||||
|
// type causer interface {
|
||||||
|
// Cause() error
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||||
|
// the topmost error which does not implement causer, which is assumed to be
|
||||||
|
// the original cause. For example:
|
||||||
|
//
|
||||||
|
// switch err := errors.Cause(err).(type) {
|
||||||
|
// case *MyError:
|
||||||
|
// // handle specifically
|
||||||
|
// default:
|
||||||
|
// // unknown error
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// causer interface is not exported by this package, but is considered a part
|
||||||
|
// of stable public API.
|
||||||
|
//
|
||||||
|
// Formatted printing of errors
|
||||||
|
//
|
||||||
|
// All error values returned from this package implement fmt.Formatter and can
|
||||||
|
// be formatted by the fmt package. The following verbs are supported
|
||||||
|
//
|
||||||
|
// %s print the error. If the error has a Cause it will be
|
||||||
|
// printed recursively
|
||||||
|
// %v see %s
|
||||||
|
// %+v extended format. Each Frame of the error's StackTrace will
|
||||||
|
// be printed in detail.
|
||||||
|
//
|
||||||
|
// Retrieving the stack trace of an error or wrapper
|
||||||
|
//
|
||||||
|
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||||
|
// invoked. This information can be retrieved with the following interface.
|
||||||
|
//
|
||||||
|
// type stackTracer interface {
|
||||||
|
// StackTrace() errors.StackTrace
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Where errors.StackTrace is defined as
|
||||||
|
//
|
||||||
|
// type StackTrace []Frame
|
||||||
|
//
|
||||||
|
// The Frame type represents a call site in the stack trace. Frame supports
|
||||||
|
// the fmt.Formatter interface that can be used for printing information about
|
||||||
|
// the stack trace of this error. For example:
|
||||||
|
//
|
||||||
|
// if err, ok := err.(stackTracer); ok {
|
||||||
|
// for _, f := range err.StackTrace() {
|
||||||
|
// fmt.Printf("%+s:%d", f)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// stackTracer interface is not exported by this package, but is considered a part
|
||||||
|
// of stable public API.
|
||||||
|
//
|
||||||
|
// See the documentation for Frame.Format for more details.
|
||||||
|
// Fork from https://github.com/pkg/errors
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns an error with the supplied message.
|
||||||
|
// New also records the stack trace at the point it was called.
|
||||||
|
func New(message string) error {
|
||||||
|
return &fundamental{
|
||||||
|
msg: message,
|
||||||
|
stack: callers(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf formats according to a format specifier and returns the string
|
||||||
|
// as a value that satisfies error.
|
||||||
|
// Errorf also records the stack trace at the point it was called.
|
||||||
|
func Errorf(format string, args ...interface{}) error {
|
||||||
|
return &fundamental{
|
||||||
|
msg: fmt.Sprintf(format, args...),
|
||||||
|
stack: callers(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fundamental is an error that has a message and a stack, but no caller.
|
||||||
|
type fundamental struct {
|
||||||
|
msg string
|
||||||
|
*stack
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fundamental) Error() string { return f.msg }
|
||||||
|
|
||||||
|
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||||
|
switch verb {
|
||||||
|
case 'v':
|
||||||
|
if s.Flag('+') {
|
||||||
|
io.WriteString(s, f.msg)
|
||||||
|
f.stack.Format(s, verb)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case 's':
|
||||||
|
io.WriteString(s, f.msg)
|
||||||
|
case 'q':
|
||||||
|
fmt.Fprintf(s, "%q", f.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||||
|
// If err is nil, WithStack returns nil.
|
||||||
|
func WithStack(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &withStack{
|
||||||
|
err,
|
||||||
|
callers(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type withStack struct {
|
||||||
|
error
|
||||||
|
*stack
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *withStack) Cause() error { return w.error }
|
||||||
|
|
||||||
|
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||||
|
switch verb {
|
||||||
|
case 'v':
|
||||||
|
if s.Flag('+') {
|
||||||
|
fmt.Fprintf(s, "%+v", w.Cause())
|
||||||
|
w.stack.Format(s, verb)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case 's':
|
||||||
|
io.WriteString(s, w.Error())
|
||||||
|
case 'q':
|
||||||
|
fmt.Fprintf(s, "%q", w.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap returns an error annotating err with a stack trace
|
||||||
|
// at the point Wrap is called, and the supplied message.
|
||||||
|
// If err is nil, Wrap returns nil.
|
||||||
|
func Wrap(err error, message string) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err = &withMessage{
|
||||||
|
cause: err,
|
||||||
|
msg: message,
|
||||||
|
}
|
||||||
|
return &withStack{
|
||||||
|
err,
|
||||||
|
callers(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapf returns an error annotating err with a stack trace
|
||||||
|
// at the point Wrapf is call, and the format specifier.
|
||||||
|
// If err is nil, Wrapf returns nil.
|
||||||
|
func Wrapf(err error, format string, args ...interface{}) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err = &withMessage{
|
||||||
|
cause: err,
|
||||||
|
msg: fmt.Sprintf(format, args...),
|
||||||
|
}
|
||||||
|
return &withStack{
|
||||||
|
err,
|
||||||
|
callers(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMessage annotates err with a new message.
|
||||||
|
// If err is nil, WithMessage returns nil.
|
||||||
|
func WithMessage(err error, message string) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &withMessage{
|
||||||
|
cause: err,
|
||||||
|
msg: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type withMessage struct {
|
||||||
|
cause error
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||||
|
func (w *withMessage) Cause() error { return w.cause }
|
||||||
|
|
||||||
|
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||||
|
switch verb {
|
||||||
|
case 'v':
|
||||||
|
if s.Flag('+') {
|
||||||
|
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||||
|
io.WriteString(s, w.msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case 's', 'q':
|
||||||
|
io.WriteString(s, w.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cause returns the underlying cause of the error, if possible.
|
||||||
|
// An error value has a cause if it implements the following
|
||||||
|
// interface:
|
||||||
|
//
|
||||||
|
// type causer interface {
|
||||||
|
// Cause() error
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If the error does not implement Cause, the original error will
|
||||||
|
// be returned. If the error is nil, nil will be returned without further
|
||||||
|
// investigation.
|
||||||
|
func Cause(err error) error {
|
||||||
|
type causer interface {
|
||||||
|
Cause() error
|
||||||
|
}
|
||||||
|
|
||||||
|
for err != nil {
|
||||||
|
cause, ok := err.(causer)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err = cause.Cause()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
187
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/errors/stack.go
generated
vendored
Normal file
187
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/errors/stack.go
generated
vendored
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
// Fork from https://github.com/pkg/errors
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Frame represents a program counter inside a stack frame.
|
||||||
|
type Frame uintptr
|
||||||
|
|
||||||
|
// pc returns the program counter for this frame;
|
||||||
|
// multiple frames may have the same PC value.
|
||||||
|
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
||||||
|
|
||||||
|
// file returns the full path to the file that contains the
|
||||||
|
// function for this Frame's pc.
|
||||||
|
func (f Frame) file() string {
|
||||||
|
fn := runtime.FuncForPC(f.pc())
|
||||||
|
if fn == nil {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
file, _ := fn.FileLine(f.pc())
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// line returns the line number of source code of the
|
||||||
|
// function for this Frame's pc.
|
||||||
|
func (f Frame) line() int {
|
||||||
|
fn := runtime.FuncForPC(f.pc())
|
||||||
|
if fn == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
_, line := fn.FileLine(f.pc())
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format formats the frame according to the fmt.Formatter interface.
|
||||||
|
//
|
||||||
|
// %s source file
|
||||||
|
// %d source line
|
||||||
|
// %n function name
|
||||||
|
// %v equivalent to %s:%d
|
||||||
|
//
|
||||||
|
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||||
|
//
|
||||||
|
// %+s path of source file relative to the compile time GOPATH
|
||||||
|
// %+v equivalent to %+s:%d
|
||||||
|
func (f Frame) Format(s fmt.State, verb rune) {
|
||||||
|
switch verb {
|
||||||
|
case 's':
|
||||||
|
switch {
|
||||||
|
case s.Flag('+'):
|
||||||
|
pc := f.pc()
|
||||||
|
fn := runtime.FuncForPC(pc)
|
||||||
|
if fn == nil {
|
||||||
|
io.WriteString(s, "unknown")
|
||||||
|
} else {
|
||||||
|
file, _ := fn.FileLine(pc)
|
||||||
|
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
io.WriteString(s, path.Base(f.file()))
|
||||||
|
}
|
||||||
|
case 'd':
|
||||||
|
fmt.Fprintf(s, "%d", f.line())
|
||||||
|
case 'n':
|
||||||
|
name := runtime.FuncForPC(f.pc()).Name()
|
||||||
|
io.WriteString(s, funcname(name))
|
||||||
|
case 'v':
|
||||||
|
f.Format(s, 's')
|
||||||
|
io.WriteString(s, ":")
|
||||||
|
f.Format(s, 'd')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||||
|
type StackTrace []Frame
|
||||||
|
|
||||||
|
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||||
|
//
|
||||||
|
// %s lists source files for each Frame in the stack
|
||||||
|
// %v lists the source file and line number for each Frame in the stack
|
||||||
|
//
|
||||||
|
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||||
|
//
|
||||||
|
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||||
|
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||||
|
switch verb {
|
||||||
|
case 'v':
|
||||||
|
switch {
|
||||||
|
case s.Flag('+'):
|
||||||
|
for _, f := range st {
|
||||||
|
fmt.Fprintf(s, "\n%+v", f)
|
||||||
|
}
|
||||||
|
case s.Flag('#'):
|
||||||
|
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(s, "%v", []Frame(st))
|
||||||
|
}
|
||||||
|
case 's':
|
||||||
|
fmt.Fprintf(s, "%s", []Frame(st))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stack represents a stack of program counters.
|
||||||
|
type stack []uintptr
|
||||||
|
|
||||||
|
func (s *stack) Format(st fmt.State, verb rune) {
|
||||||
|
switch verb {
|
||||||
|
case 'v':
|
||||||
|
switch {
|
||||||
|
case st.Flag('+'):
|
||||||
|
for _, pc := range *s {
|
||||||
|
f := Frame(pc)
|
||||||
|
fmt.Fprintf(st, "\n%+v", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stack) StackTrace() StackTrace {
|
||||||
|
f := make([]Frame, len(*s))
|
||||||
|
for i := 0; i < len(f); i++ {
|
||||||
|
f[i] = Frame((*s)[i])
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func callers() *stack {
|
||||||
|
const depth = 32
|
||||||
|
var pcs [depth]uintptr
|
||||||
|
n := runtime.Callers(3, pcs[:])
|
||||||
|
var st stack = pcs[0:n]
|
||||||
|
return &st
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||||
|
func funcname(name string) string {
|
||||||
|
i := strings.LastIndex(name, "/")
|
||||||
|
name = name[i+1:]
|
||||||
|
i = strings.Index(name, ".")
|
||||||
|
return name[i+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimGOPATH(name, file string) string {
|
||||||
|
// Here we want to get the source file path relative to the compile time
|
||||||
|
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
|
||||||
|
// GOPATH at runtime, but we can infer the number of path segments in the
|
||||||
|
// GOPATH. We note that fn.Name() returns the function name qualified by
|
||||||
|
// the import path, which does not include the GOPATH. Thus we can trim
|
||||||
|
// segments from the beginning of the file path until the number of path
|
||||||
|
// separators remaining is one more than the number of path separators in
|
||||||
|
// the function name. For example, given:
|
||||||
|
//
|
||||||
|
// GOPATH /home/user
|
||||||
|
// file /home/user/src/pkg/sub/file.go
|
||||||
|
// fn.Name() pkg/sub.Type.Method
|
||||||
|
//
|
||||||
|
// We want to produce:
|
||||||
|
//
|
||||||
|
// pkg/sub/file.go
|
||||||
|
//
|
||||||
|
// From this we can easily see that fn.Name() has one less path separator
|
||||||
|
// than our desired output. We count separators from the end of the file
|
||||||
|
// path until it finds two more than in the function name and then move
|
||||||
|
// one character forward to preserve the initial path segment without a
|
||||||
|
// leading separator.
|
||||||
|
const sep = "/"
|
||||||
|
goal := strings.Count(name, sep) + 2
|
||||||
|
i := len(file)
|
||||||
|
for n := 0; n < goal; n++ {
|
||||||
|
i = strings.LastIndex(file[:i], sep)
|
||||||
|
if i == -1 {
|
||||||
|
// not enough separators found, set i so that the slice expression
|
||||||
|
// below leaves file unmodified
|
||||||
|
i = -len(sep)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// get back to 0 or trim the leading separator
|
||||||
|
file = file[i+len(sep):]
|
||||||
|
return file
|
||||||
|
}
|
86
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/logger/go17.go
generated
vendored
Normal file
86
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/logger/go17.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013-2017 Oryx(ossrs)
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *loggerPlus) Println(ctx Context, a ...interface{}) {
|
||||||
|
args := v.contextFormat(ctx, a...)
|
||||||
|
v.doPrintln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *loggerPlus) Printf(ctx Context, format string, a ...interface{}) {
|
||||||
|
format, args := v.contextFormatf(ctx, format, a...)
|
||||||
|
v.doPrintf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *loggerPlus) contextFormat(ctx Context, a ...interface{}) []interface{} {
|
||||||
|
if ctx, ok := ctx.(context.Context); ok {
|
||||||
|
if cid, ok := ctx.Value(cidKey).(int); ok {
|
||||||
|
return append([]interface{}{fmt.Sprintf("[%v][%v]", os.Getpid(), cid)}, a...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return v.format(ctx, a...)
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *loggerPlus) contextFormatf(ctx Context, format string, a ...interface{}) (string, []interface{}) {
|
||||||
|
if ctx, ok := ctx.(context.Context); ok {
|
||||||
|
if cid, ok := ctx.Value(cidKey).(int); ok {
|
||||||
|
return "[%v][%v] " + format, append([]interface{}{os.Getpid(), cid}, a...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return v.formatf(ctx, format, a...)
|
||||||
|
}
|
||||||
|
return format, a
|
||||||
|
}
|
||||||
|
|
||||||
|
// User should use context with value to pass the cid.
|
||||||
|
type key string
|
||||||
|
|
||||||
|
var cidKey key = "cid.logger.ossrs.org"
|
||||||
|
|
||||||
|
var gCid int = 999
|
||||||
|
|
||||||
|
// Create context with value.
|
||||||
|
func WithContext(ctx context.Context) context.Context {
|
||||||
|
gCid += 1
|
||||||
|
return context.WithValue(ctx, cidKey, gCid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create context with value from parent, copy the cid from source context.
|
||||||
|
// @remark Create new cid if source has no cid represent.
|
||||||
|
func AliasContext(parent context.Context, source context.Context) context.Context {
|
||||||
|
if source != nil {
|
||||||
|
if cid, ok := source.Value(cidKey).(int); ok {
|
||||||
|
return context.WithValue(parent, cidKey, cid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WithContext(parent)
|
||||||
|
}
|
239
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/logger/logger.go
generated
vendored
Normal file
239
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/logger/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013-2017 Oryx(ossrs)
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// The oryx logger package provides connection-oriented log service.
|
||||||
|
// logger.I(ctx, ...)
|
||||||
|
// logger.T(ctx, ...)
|
||||||
|
// logger.W(ctx, ...)
|
||||||
|
// logger.E(ctx, ...)
|
||||||
|
// Or use format:
|
||||||
|
// logger.If(ctx, format, ...)
|
||||||
|
// logger.Tf(ctx, format, ...)
|
||||||
|
// logger.Wf(ctx, format, ...)
|
||||||
|
// logger.Ef(ctx, format, ...)
|
||||||
|
// @remark the Context is optional thus can be nil.
|
||||||
|
// @remark From 1.7+, the ctx could be context.Context, wrap by logger.WithContext,
|
||||||
|
// please read ExampleLogger_ContextGO17().
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// default level for logger.
|
||||||
|
const (
|
||||||
|
logInfoLabel = "[info] "
|
||||||
|
logTraceLabel = "[trace] "
|
||||||
|
logWarnLabel = "[warn] "
|
||||||
|
logErrorLabel = "[error] "
|
||||||
|
)
|
||||||
|
|
||||||
|
// The context for current goroutine.
|
||||||
|
// It maybe a cidContext or context.Context from GO1.7.
|
||||||
|
// @remark Use logger.WithContext(ctx) to wrap the context.
|
||||||
|
type Context interface{}
|
||||||
|
|
||||||
|
// The context to get current coroutine cid.
|
||||||
|
type cidContext interface {
|
||||||
|
Cid() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// the LOG+ which provides connection-based log.
|
||||||
|
type loggerPlus struct {
|
||||||
|
logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoggerPlus(l *log.Logger) Logger {
|
||||||
|
return &loggerPlus{logger: l}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *loggerPlus) format(ctx Context, a ...interface{}) []interface{} {
|
||||||
|
if ctx == nil {
|
||||||
|
return append([]interface{}{fmt.Sprintf("[%v] ", os.Getpid())}, a...)
|
||||||
|
} else if ctx, ok := ctx.(cidContext); ok {
|
||||||
|
return append([]interface{}{fmt.Sprintf("[%v][%v] ", os.Getpid(), ctx.Cid())}, a...)
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *loggerPlus) formatf(ctx Context, format string, a ...interface{}) (string, []interface{}) {
|
||||||
|
if ctx == nil {
|
||||||
|
return "[%v] " + format, append([]interface{}{os.Getpid()}, a...)
|
||||||
|
} else if ctx, ok := ctx.(cidContext); ok {
|
||||||
|
return "[%v][%v] " + format, append([]interface{}{os.Getpid(), ctx.Cid()}, a...)
|
||||||
|
}
|
||||||
|
return format, a
|
||||||
|
}
|
||||||
|
|
||||||
|
var colorYellow = "\033[33m"
|
||||||
|
var colorRed = "\033[31m"
|
||||||
|
var colorBlack = "\033[0m"
|
||||||
|
|
||||||
|
func (v *loggerPlus) doPrintln(args ...interface{}) {
|
||||||
|
if previousCloser == nil {
|
||||||
|
if v == Error {
|
||||||
|
fmt.Fprintf(os.Stdout, colorRed)
|
||||||
|
v.logger.Println(args...)
|
||||||
|
fmt.Fprintf(os.Stdout, colorBlack)
|
||||||
|
} else if v == Warn {
|
||||||
|
fmt.Fprintf(os.Stdout, colorYellow)
|
||||||
|
v.logger.Println(args...)
|
||||||
|
fmt.Fprintf(os.Stdout, colorBlack)
|
||||||
|
} else {
|
||||||
|
v.logger.Println(args...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v.logger.Println(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *loggerPlus) doPrintf(format string, args ...interface{}) {
|
||||||
|
if previousCloser == nil {
|
||||||
|
if v == Error {
|
||||||
|
fmt.Fprintf(os.Stdout, colorRed)
|
||||||
|
v.logger.Printf(format, args...)
|
||||||
|
fmt.Fprintf(os.Stdout, colorBlack)
|
||||||
|
} else if v == Warn {
|
||||||
|
fmt.Fprintf(os.Stdout, colorYellow)
|
||||||
|
v.logger.Printf(format, args...)
|
||||||
|
fmt.Fprintf(os.Stdout, colorBlack)
|
||||||
|
} else {
|
||||||
|
v.logger.Printf(format, args...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v.logger.Printf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info, the verbose info level, very detail log, the lowest level, to discard.
|
||||||
|
var Info Logger
|
||||||
|
|
||||||
|
// Alias for Info level println.
|
||||||
|
func I(ctx Context, a ...interface{}) {
|
||||||
|
Info.Println(ctx, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf for Info level log.
|
||||||
|
func If(ctx Context, format string, a ...interface{}) {
|
||||||
|
Info.Printf(ctx, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace, the trace level, something important, the default log level, to stdout.
|
||||||
|
var Trace Logger
|
||||||
|
|
||||||
|
// Alias for Trace level println.
|
||||||
|
func T(ctx Context, a ...interface{}) {
|
||||||
|
Trace.Println(ctx, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf for Trace level log.
|
||||||
|
func Tf(ctx Context, format string, a ...interface{}) {
|
||||||
|
Trace.Printf(ctx, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn, the warning level, dangerous information, to Stdout.
|
||||||
|
var Warn Logger
|
||||||
|
|
||||||
|
// Alias for Warn level println.
|
||||||
|
func W(ctx Context, a ...interface{}) {
|
||||||
|
Warn.Println(ctx, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf for Warn level log.
|
||||||
|
func Wf(ctx Context, format string, a ...interface{}) {
|
||||||
|
Warn.Printf(ctx, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error, the error level, fatal error things, ot Stdout.
|
||||||
|
var Error Logger
|
||||||
|
|
||||||
|
// Alias for Error level println.
|
||||||
|
func E(ctx Context, a ...interface{}) {
|
||||||
|
Error.Println(ctx, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf for Error level log.
|
||||||
|
func Ef(ctx Context, format string, a ...interface{}) {
|
||||||
|
Error.Printf(ctx, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The logger for oryx.
|
||||||
|
type Logger interface {
|
||||||
|
// Println for logger plus,
|
||||||
|
// @param ctx the connection-oriented context,
|
||||||
|
// or context.Context from GO1.7, or nil to ignore.
|
||||||
|
Println(ctx Context, a ...interface{})
|
||||||
|
Printf(ctx Context, format string, a ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Info = NewLoggerPlus(log.New(ioutil.Discard, logInfoLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
Trace = NewLoggerPlus(log.New(os.Stdout, logTraceLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
Warn = NewLoggerPlus(log.New(os.Stderr, logWarnLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
Error = NewLoggerPlus(log.New(os.Stderr, logErrorLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
|
||||||
|
// init writer and closer.
|
||||||
|
previousWriter = os.Stdout
|
||||||
|
previousCloser = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch the underlayer io.
|
||||||
|
// @remark user must close previous io for logger never close it.
|
||||||
|
func Switch(w io.Writer) io.Writer {
|
||||||
|
// TODO: support level, default to trace here.
|
||||||
|
Info = NewLoggerPlus(log.New(ioutil.Discard, logInfoLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
Trace = NewLoggerPlus(log.New(w, logTraceLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
Warn = NewLoggerPlus(log.New(w, logWarnLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
Error = NewLoggerPlus(log.New(w, logErrorLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
|
||||||
|
ow := previousWriter
|
||||||
|
previousWriter = w
|
||||||
|
|
||||||
|
if c, ok := w.(io.Closer); ok {
|
||||||
|
previousCloser = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return ow
|
||||||
|
}
|
||||||
|
|
||||||
|
// The previous underlayer io for logger.
|
||||||
|
var previousCloser io.Closer
|
||||||
|
var previousWriter io.Writer
|
||||||
|
|
||||||
|
// The interface io.Closer
|
||||||
|
// Cleanup the logger, discard any log util switch to fresh writer.
|
||||||
|
func Close() (err error) {
|
||||||
|
Info = NewLoggerPlus(log.New(ioutil.Discard, logInfoLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
Trace = NewLoggerPlus(log.New(ioutil.Discard, logTraceLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
Warn = NewLoggerPlus(log.New(ioutil.Discard, logWarnLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
Error = NewLoggerPlus(log.New(ioutil.Discard, logErrorLabel, log.Ldate|log.Ltime|log.Lmicroseconds))
|
||||||
|
|
||||||
|
if previousCloser != nil {
|
||||||
|
err = previousCloser.Close()
|
||||||
|
previousCloser = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
34
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/logger/pre_go17.go
generated
vendored
Normal file
34
trunk/3rdparty/signaling/vendor/github.com/ossrs/go-oryx-lib/logger/pre_go17.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013-2017 Oryx(ossrs)
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build !go1.7
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
func (v *loggerPlus) Println(ctx Context, a ...interface{}) {
|
||||||
|
args := v.format(ctx, a...)
|
||||||
|
v.doPrintln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *loggerPlus) Printf(ctx Context, format string, a ...interface{}) {
|
||||||
|
format, args := v.formatf(ctx, format, a...)
|
||||||
|
v.doPrintf(format, args...)
|
||||||
|
}
|
3
trunk/3rdparty/signaling/vendor/golang.org/x/net/AUTHORS
generated
vendored
Normal file
3
trunk/3rdparty/signaling/vendor/golang.org/x/net/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
3
trunk/3rdparty/signaling/vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
Normal file
3
trunk/3rdparty/signaling/vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
27
trunk/3rdparty/signaling/vendor/golang.org/x/net/LICENSE
generated
vendored
Normal file
27
trunk/3rdparty/signaling/vendor/golang.org/x/net/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
trunk/3rdparty/signaling/vendor/golang.org/x/net/PATENTS
generated
vendored
Normal file
22
trunk/3rdparty/signaling/vendor/golang.org/x/net/PATENTS
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||||
|
patent license to make, have made, use, offer to sell, sell, import,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
106
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/client.go
generated
vendored
Normal file
106
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/client.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// Copyright 2009 The Go 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"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DialError is an error that occurs while dialling a websocket server.
|
||||||
|
type DialError struct {
|
||||||
|
*Config
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DialError) Error() string {
|
||||||
|
return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfig creates a new WebSocket config for client connection.
|
||||||
|
func NewConfig(server, origin string) (config *Config, err error) {
|
||||||
|
config = new(Config)
|
||||||
|
config.Version = ProtocolVersionHybi13
|
||||||
|
config.Location, err = url.ParseRequestURI(server)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config.Origin, err = url.ParseRequestURI(origin)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config.Header = http.Header(make(map[string][]string))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient creates a new WebSocket client connection over rwc.
|
||||||
|
func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
|
||||||
|
br := bufio.NewReader(rwc)
|
||||||
|
bw := bufio.NewWriter(rwc)
|
||||||
|
err = hybiClientHandshake(config, br, bw)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf := bufio.NewReadWriter(br, bw)
|
||||||
|
ws = newHybiClientConn(config, buf, rwc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial opens a new client connection to a WebSocket.
|
||||||
|
func Dial(url_, protocol, origin string) (ws *Conn, err error) {
|
||||||
|
config, err := NewConfig(url_, origin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if protocol != "" {
|
||||||
|
config.Protocol = []string{protocol}
|
||||||
|
}
|
||||||
|
return DialConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
var portMap = map[string]string{
|
||||||
|
"ws": "80",
|
||||||
|
"wss": "443",
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAuthority(location *url.URL) string {
|
||||||
|
if _, ok := portMap[location.Scheme]; ok {
|
||||||
|
if _, _, err := net.SplitHostPort(location.Host); err != nil {
|
||||||
|
return net.JoinHostPort(location.Host, portMap[location.Scheme])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return location.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialConfig opens a new client connection to a WebSocket with a config.
|
||||||
|
func DialConfig(config *Config) (ws *Conn, err error) {
|
||||||
|
var client net.Conn
|
||||||
|
if config.Location == nil {
|
||||||
|
return nil, &DialError{config, ErrBadWebSocketLocation}
|
||||||
|
}
|
||||||
|
if config.Origin == nil {
|
||||||
|
return nil, &DialError{config, ErrBadWebSocketOrigin}
|
||||||
|
}
|
||||||
|
dialer := config.Dialer
|
||||||
|
if dialer == nil {
|
||||||
|
dialer = &net.Dialer{}
|
||||||
|
}
|
||||||
|
client, err = dialWithDialer(dialer, config)
|
||||||
|
if err != nil {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
ws, err = NewClient(config, client)
|
||||||
|
if err != nil {
|
||||||
|
client.Close()
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
Error:
|
||||||
|
return nil, &DialError{config, err}
|
||||||
|
}
|
24
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/dial.go
generated
vendored
Normal file
24
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/dial.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2015 The Go 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"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dialWithDialer(dialer *net.Dialer, config *Config) (conn net.Conn, err error) {
|
||||||
|
switch config.Location.Scheme {
|
||||||
|
case "ws":
|
||||||
|
conn, err = dialer.Dial("tcp", parseAuthority(config.Location))
|
||||||
|
|
||||||
|
case "wss":
|
||||||
|
conn, err = tls.DialWithDialer(dialer, "tcp", parseAuthority(config.Location), config.TlsConfig)
|
||||||
|
|
||||||
|
default:
|
||||||
|
err = ErrBadScheme
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
583
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/hybi.go
generated
vendored
Normal file
583
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/hybi.go
generated
vendored
Normal file
|
@ -0,0 +1,583 @@
|
||||||
|
// Copyright 2011 The Go 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
|
||||||
|
|
||||||
|
// This file implements a protocol of hybi draft.
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||||
|
|
||||||
|
closeStatusNormal = 1000
|
||||||
|
closeStatusGoingAway = 1001
|
||||||
|
closeStatusProtocolError = 1002
|
||||||
|
closeStatusUnsupportedData = 1003
|
||||||
|
closeStatusFrameTooLarge = 1004
|
||||||
|
closeStatusNoStatusRcvd = 1005
|
||||||
|
closeStatusAbnormalClosure = 1006
|
||||||
|
closeStatusBadMessageData = 1007
|
||||||
|
closeStatusPolicyViolation = 1008
|
||||||
|
closeStatusTooBigData = 1009
|
||||||
|
closeStatusExtensionMismatch = 1010
|
||||||
|
|
||||||
|
maxControlFramePayloadLength = 125
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrBadMaskingKey = &ProtocolError{"bad masking key"}
|
||||||
|
ErrBadPongMessage = &ProtocolError{"bad pong message"}
|
||||||
|
ErrBadClosingStatus = &ProtocolError{"bad closing status"}
|
||||||
|
ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"}
|
||||||
|
ErrNotImplemented = &ProtocolError{"not implemented"}
|
||||||
|
|
||||||
|
handshakeHeader = map[string]bool{
|
||||||
|
"Host": true,
|
||||||
|
"Upgrade": true,
|
||||||
|
"Connection": true,
|
||||||
|
"Sec-Websocket-Key": true,
|
||||||
|
"Sec-Websocket-Origin": true,
|
||||||
|
"Sec-Websocket-Version": true,
|
||||||
|
"Sec-Websocket-Protocol": true,
|
||||||
|
"Sec-Websocket-Accept": true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// A hybiFrameHeader is a frame header as defined in hybi draft.
|
||||||
|
type hybiFrameHeader struct {
|
||||||
|
Fin bool
|
||||||
|
Rsv [3]bool
|
||||||
|
OpCode byte
|
||||||
|
Length int64
|
||||||
|
MaskingKey []byte
|
||||||
|
|
||||||
|
data *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// A hybiFrameReader is a reader for hybi frame.
|
||||||
|
type hybiFrameReader struct {
|
||||||
|
reader io.Reader
|
||||||
|
|
||||||
|
header hybiFrameHeader
|
||||||
|
pos int64
|
||||||
|
length int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
|
||||||
|
n, err = frame.reader.Read(msg)
|
||||||
|
if frame.header.MaskingKey != nil {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4]
|
||||||
|
frame.pos++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
|
||||||
|
|
||||||
|
func (frame *hybiFrameReader) HeaderReader() io.Reader {
|
||||||
|
if frame.header.data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if frame.header.data.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return frame.header.data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
|
||||||
|
|
||||||
|
func (frame *hybiFrameReader) Len() (n int) { return frame.length }
|
||||||
|
|
||||||
|
// A hybiFrameReaderFactory creates new frame reader based on its frame type.
|
||||||
|
type hybiFrameReaderFactory struct {
|
||||||
|
*bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
|
||||||
|
// See Section 5.2 Base Framing protocol for detail.
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
|
||||||
|
func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
|
||||||
|
hybiFrame := new(hybiFrameReader)
|
||||||
|
frame = hybiFrame
|
||||||
|
var header []byte
|
||||||
|
var b byte
|
||||||
|
// First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
|
||||||
|
b, err = buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header = append(header, b)
|
||||||
|
hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
j := uint(6 - i)
|
||||||
|
hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0
|
||||||
|
}
|
||||||
|
hybiFrame.header.OpCode = header[0] & 0x0f
|
||||||
|
|
||||||
|
// Second byte. Mask/Payload len(7bits)
|
||||||
|
b, err = buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header = append(header, b)
|
||||||
|
mask := (b & 0x80) != 0
|
||||||
|
b &= 0x7f
|
||||||
|
lengthFields := 0
|
||||||
|
switch {
|
||||||
|
case b <= 125: // Payload length 7bits.
|
||||||
|
hybiFrame.header.Length = int64(b)
|
||||||
|
case b == 126: // Payload length 7+16bits
|
||||||
|
lengthFields = 2
|
||||||
|
case b == 127: // Payload length 7+64bits
|
||||||
|
lengthFields = 8
|
||||||
|
}
|
||||||
|
for i := 0; i < lengthFields; i++ {
|
||||||
|
b, err = buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lengthFields == 8 && i == 0 { // MSB must be zero when 7+64 bits
|
||||||
|
b &= 0x7f
|
||||||
|
}
|
||||||
|
header = append(header, b)
|
||||||
|
hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b)
|
||||||
|
}
|
||||||
|
if mask {
|
||||||
|
// Masking key. 4 bytes.
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
b, err = buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header = append(header, b)
|
||||||
|
hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length)
|
||||||
|
hybiFrame.header.data = bytes.NewBuffer(header)
|
||||||
|
hybiFrame.length = len(header) + int(hybiFrame.header.Length)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A HybiFrameWriter is a writer for hybi frame.
|
||||||
|
type hybiFrameWriter struct {
|
||||||
|
writer *bufio.Writer
|
||||||
|
|
||||||
|
header *hybiFrameHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
|
||||||
|
var header []byte
|
||||||
|
var b byte
|
||||||
|
if frame.header.Fin {
|
||||||
|
b |= 0x80
|
||||||
|
}
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
if frame.header.Rsv[i] {
|
||||||
|
j := uint(6 - i)
|
||||||
|
b |= 1 << j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b |= frame.header.OpCode
|
||||||
|
header = append(header, b)
|
||||||
|
if frame.header.MaskingKey != nil {
|
||||||
|
b = 0x80
|
||||||
|
} else {
|
||||||
|
b = 0
|
||||||
|
}
|
||||||
|
lengthFields := 0
|
||||||
|
length := len(msg)
|
||||||
|
switch {
|
||||||
|
case length <= 125:
|
||||||
|
b |= byte(length)
|
||||||
|
case length < 65536:
|
||||||
|
b |= 126
|
||||||
|
lengthFields = 2
|
||||||
|
default:
|
||||||
|
b |= 127
|
||||||
|
lengthFields = 8
|
||||||
|
}
|
||||||
|
header = append(header, b)
|
||||||
|
for i := 0; i < lengthFields; i++ {
|
||||||
|
j := uint((lengthFields - i - 1) * 8)
|
||||||
|
b = byte((length >> j) & 0xff)
|
||||||
|
header = append(header, b)
|
||||||
|
}
|
||||||
|
if frame.header.MaskingKey != nil {
|
||||||
|
if len(frame.header.MaskingKey) != 4 {
|
||||||
|
return 0, ErrBadMaskingKey
|
||||||
|
}
|
||||||
|
header = append(header, frame.header.MaskingKey...)
|
||||||
|
frame.writer.Write(header)
|
||||||
|
data := make([]byte, length)
|
||||||
|
for i := range data {
|
||||||
|
data[i] = msg[i] ^ frame.header.MaskingKey[i%4]
|
||||||
|
}
|
||||||
|
frame.writer.Write(data)
|
||||||
|
err = frame.writer.Flush()
|
||||||
|
return length, err
|
||||||
|
}
|
||||||
|
frame.writer.Write(header)
|
||||||
|
frame.writer.Write(msg)
|
||||||
|
err = frame.writer.Flush()
|
||||||
|
return length, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *hybiFrameWriter) Close() error { return nil }
|
||||||
|
|
||||||
|
type hybiFrameWriterFactory struct {
|
||||||
|
*bufio.Writer
|
||||||
|
needMaskingKey bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
|
||||||
|
frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
|
||||||
|
if buf.needMaskingKey {
|
||||||
|
frameHeader.MaskingKey, err = generateMaskingKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type hybiFrameHandler struct {
|
||||||
|
conn *Conn
|
||||||
|
payloadType byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (frameReader, error) {
|
||||||
|
if handler.conn.IsServerConn() {
|
||||||
|
// The client MUST mask all frames sent to the server.
|
||||||
|
if frame.(*hybiFrameReader).header.MaskingKey == nil {
|
||||||
|
handler.WriteClose(closeStatusProtocolError)
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The server MUST NOT mask all frames.
|
||||||
|
if frame.(*hybiFrameReader).header.MaskingKey != nil {
|
||||||
|
handler.WriteClose(closeStatusProtocolError)
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if header := frame.HeaderReader(); header != nil {
|
||||||
|
io.Copy(ioutil.Discard, header)
|
||||||
|
}
|
||||||
|
switch frame.PayloadType() {
|
||||||
|
case ContinuationFrame:
|
||||||
|
frame.(*hybiFrameReader).header.OpCode = handler.payloadType
|
||||||
|
case TextFrame, BinaryFrame:
|
||||||
|
handler.payloadType = frame.PayloadType()
|
||||||
|
case CloseFrame:
|
||||||
|
return nil, io.EOF
|
||||||
|
case PingFrame, PongFrame:
|
||||||
|
b := make([]byte, maxControlFramePayloadLength)
|
||||||
|
n, err := io.ReadFull(frame, b)
|
||||||
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
io.Copy(ioutil.Discard, frame)
|
||||||
|
if frame.PayloadType() == PingFrame {
|
||||||
|
if _, err := handler.WritePong(b[:n]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return frame, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
|
||||||
|
handler.conn.wio.Lock()
|
||||||
|
defer handler.conn.wio.Unlock()
|
||||||
|
w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg := make([]byte, 2)
|
||||||
|
binary.BigEndian.PutUint16(msg, uint16(status))
|
||||||
|
_, err = w.Write(msg)
|
||||||
|
w.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
|
||||||
|
handler.conn.wio.Lock()
|
||||||
|
defer handler.conn.wio.Unlock()
|
||||||
|
w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
n, err = w.Write(msg)
|
||||||
|
w.Close()
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
|
||||||
|
func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
||||||
|
if buf == nil {
|
||||||
|
br := bufio.NewReader(rwc)
|
||||||
|
bw := bufio.NewWriter(rwc)
|
||||||
|
buf = bufio.NewReadWriter(br, bw)
|
||||||
|
}
|
||||||
|
ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
|
||||||
|
frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
|
||||||
|
frameWriterFactory: hybiFrameWriterFactory{
|
||||||
|
buf.Writer, request == nil},
|
||||||
|
PayloadType: TextFrame,
|
||||||
|
defaultCloseStatus: closeStatusNormal}
|
||||||
|
ws.frameHandler = &hybiFrameHandler{conn: ws}
|
||||||
|
return ws
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateMaskingKey generates a masking key for a frame.
|
||||||
|
func generateMaskingKey() (maskingKey []byte, err error) {
|
||||||
|
maskingKey = make([]byte, 4)
|
||||||
|
if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateNonce generates a nonce consisting of a randomly selected 16-byte
|
||||||
|
// value that has been base64-encoded.
|
||||||
|
func generateNonce() (nonce []byte) {
|
||||||
|
key := make([]byte, 16)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
nonce = make([]byte, 24)
|
||||||
|
base64.StdEncoding.Encode(nonce, key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeZone removes IPv6 zone identifer from host.
|
||||||
|
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
|
||||||
|
func removeZone(host string) string {
|
||||||
|
if !strings.HasPrefix(host, "[") {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
i := strings.LastIndex(host, "]")
|
||||||
|
if i < 0 {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
j := strings.LastIndex(host[:i], "%")
|
||||||
|
if j < 0 {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
return host[:j] + host[i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
|
||||||
|
// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
|
||||||
|
func getNonceAccept(nonce []byte) (expected []byte, err error) {
|
||||||
|
h := sha1.New()
|
||||||
|
if _, err = h.Write(nonce); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = h.Write([]byte(websocketGUID)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expected = make([]byte, 28)
|
||||||
|
base64.StdEncoding.Encode(expected, h.Sum(nil))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
|
||||||
|
func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
|
||||||
|
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
|
||||||
|
|
||||||
|
// According to RFC 6874, an HTTP client, proxy, or other
|
||||||
|
// intermediary must remove any IPv6 zone identifier attached
|
||||||
|
// to an outgoing URI.
|
||||||
|
bw.WriteString("Host: " + removeZone(config.Location.Host) + "\r\n")
|
||||||
|
bw.WriteString("Upgrade: websocket\r\n")
|
||||||
|
bw.WriteString("Connection: Upgrade\r\n")
|
||||||
|
nonce := generateNonce()
|
||||||
|
if config.handshakeData != nil {
|
||||||
|
nonce = []byte(config.handshakeData["key"])
|
||||||
|
}
|
||||||
|
bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
|
||||||
|
bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
|
||||||
|
|
||||||
|
if config.Version != ProtocolVersionHybi13 {
|
||||||
|
return ErrBadProtocolVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
|
||||||
|
if len(config.Protocol) > 0 {
|
||||||
|
bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
|
||||||
|
}
|
||||||
|
// TODO(ukai): send Sec-WebSocket-Extensions.
|
||||||
|
err = config.Header.WriteSubset(bw, handshakeHeader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bw.WriteString("\r\n")
|
||||||
|
if err = bw.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 101 {
|
||||||
|
return ErrBadStatus
|
||||||
|
}
|
||||||
|
if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
|
||||||
|
strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
|
||||||
|
return ErrBadUpgrade
|
||||||
|
}
|
||||||
|
expectedAccept, err := getNonceAccept(nonce)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
|
||||||
|
return ErrChallengeResponse
|
||||||
|
}
|
||||||
|
if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
|
||||||
|
return ErrUnsupportedExtensions
|
||||||
|
}
|
||||||
|
offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
|
||||||
|
if offeredProtocol != "" {
|
||||||
|
protocolMatched := false
|
||||||
|
for i := 0; i < len(config.Protocol); i++ {
|
||||||
|
if config.Protocol[i] == offeredProtocol {
|
||||||
|
protocolMatched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !protocolMatched {
|
||||||
|
return ErrBadWebSocketProtocol
|
||||||
|
}
|
||||||
|
config.Protocol = []string{offeredProtocol}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHybiClientConn creates a client WebSocket connection after handshake.
|
||||||
|
func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
|
||||||
|
return newHybiConn(config, buf, rwc, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
|
||||||
|
type hybiServerHandshaker struct {
|
||||||
|
*Config
|
||||||
|
accept []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
|
||||||
|
c.Version = ProtocolVersionHybi13
|
||||||
|
if req.Method != "GET" {
|
||||||
|
return http.StatusMethodNotAllowed, ErrBadRequestMethod
|
||||||
|
}
|
||||||
|
// HTTP version can be safely ignored.
|
||||||
|
|
||||||
|
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
|
||||||
|
!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
|
||||||
|
return http.StatusBadRequest, ErrNotWebSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
key := req.Header.Get("Sec-Websocket-Key")
|
||||||
|
if key == "" {
|
||||||
|
return http.StatusBadRequest, ErrChallengeResponse
|
||||||
|
}
|
||||||
|
version := req.Header.Get("Sec-Websocket-Version")
|
||||||
|
switch version {
|
||||||
|
case "13":
|
||||||
|
c.Version = ProtocolVersionHybi13
|
||||||
|
default:
|
||||||
|
return http.StatusBadRequest, ErrBadWebSocketVersion
|
||||||
|
}
|
||||||
|
var scheme string
|
||||||
|
if req.TLS != nil {
|
||||||
|
scheme = "wss"
|
||||||
|
} else {
|
||||||
|
scheme = "ws"
|
||||||
|
}
|
||||||
|
c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI())
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusBadRequest, err
|
||||||
|
}
|
||||||
|
protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
|
||||||
|
if protocol != "" {
|
||||||
|
protocols := strings.Split(protocol, ",")
|
||||||
|
for i := 0; i < len(protocols); i++ {
|
||||||
|
c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.accept, err = getNonceAccept([]byte(key))
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
return http.StatusSwitchingProtocols, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Origin parses the Origin header in req.
|
||||||
|
// If the Origin header is not set, it returns nil and nil.
|
||||||
|
func Origin(config *Config, req *http.Request) (*url.URL, error) {
|
||||||
|
var origin string
|
||||||
|
switch config.Version {
|
||||||
|
case ProtocolVersionHybi13:
|
||||||
|
origin = req.Header.Get("Origin")
|
||||||
|
}
|
||||||
|
if origin == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return url.ParseRequestURI(origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
|
||||||
|
if len(c.Protocol) > 0 {
|
||||||
|
if len(c.Protocol) != 1 {
|
||||||
|
// You need choose a Protocol in Handshake func in Server.
|
||||||
|
return ErrBadWebSocketProtocol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
|
||||||
|
buf.WriteString("Upgrade: websocket\r\n")
|
||||||
|
buf.WriteString("Connection: Upgrade\r\n")
|
||||||
|
buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
|
||||||
|
if len(c.Protocol) > 0 {
|
||||||
|
buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
|
||||||
|
}
|
||||||
|
// TODO(ukai): send Sec-WebSocket-Extensions.
|
||||||
|
if c.Header != nil {
|
||||||
|
err := c.Header.WriteSubset(buf, handshakeHeader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString("\r\n")
|
||||||
|
return buf.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
||||||
|
return newHybiServerConn(c.Config, buf, rwc, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
|
||||||
|
func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
||||||
|
return newHybiConn(config, buf, rwc, request)
|
||||||
|
}
|
113
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/server.go
generated
vendored
Normal file
113
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/server.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
// Copyright 2009 The Go 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"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) {
|
||||||
|
var hs serverHandshaker = &hybiServerHandshaker{Config: config}
|
||||||
|
code, err := hs.ReadHandshake(buf.Reader, req)
|
||||||
|
if err == ErrBadWebSocketVersion {
|
||||||
|
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||||
|
fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion)
|
||||||
|
buf.WriteString("\r\n")
|
||||||
|
buf.WriteString(err.Error())
|
||||||
|
buf.Flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||||
|
buf.WriteString("\r\n")
|
||||||
|
buf.WriteString(err.Error())
|
||||||
|
buf.Flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if handshake != nil {
|
||||||
|
err = handshake(config, req)
|
||||||
|
if err != nil {
|
||||||
|
code = http.StatusForbidden
|
||||||
|
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||||
|
buf.WriteString("\r\n")
|
||||||
|
buf.Flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = hs.AcceptHandshake(buf.Writer)
|
||||||
|
if err != nil {
|
||||||
|
code = http.StatusBadRequest
|
||||||
|
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||||
|
buf.WriteString("\r\n")
|
||||||
|
buf.Flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn = hs.NewServerConn(buf, rwc, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server represents a server of a WebSocket.
|
||||||
|
type Server struct {
|
||||||
|
// Config is a WebSocket configuration for new WebSocket connection.
|
||||||
|
Config
|
||||||
|
|
||||||
|
// Handshake is an optional function in WebSocket handshake.
|
||||||
|
// For example, you can check, or don't check Origin header.
|
||||||
|
// Another example, you can select config.Protocol.
|
||||||
|
Handshake func(*Config, *http.Request) error
|
||||||
|
|
||||||
|
// Handler handles a WebSocket connection.
|
||||||
|
Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP implements the http.Handler interface for a WebSocket
|
||||||
|
func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
s.serveWebSocket(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) {
|
||||||
|
rwc, buf, err := w.(http.Hijacker).Hijack()
|
||||||
|
if err != nil {
|
||||||
|
panic("Hijack failed: " + err.Error())
|
||||||
|
}
|
||||||
|
// The server should abort the WebSocket connection if it finds
|
||||||
|
// the client did not send a handshake that matches with protocol
|
||||||
|
// specification.
|
||||||
|
defer rwc.Close()
|
||||||
|
conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if conn == nil {
|
||||||
|
panic("unexpected nil conn")
|
||||||
|
}
|
||||||
|
s.Handler(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler is a simple interface to a WebSocket browser client.
|
||||||
|
// It checks if Origin header is valid URL by default.
|
||||||
|
// You might want to verify websocket.Conn.Config().Origin in the func.
|
||||||
|
// If you use Server instead of Handler, you could call websocket.Origin and
|
||||||
|
// check the origin in your Handshake func. So, if you want to accept
|
||||||
|
// non-browser clients, which do not send an Origin header, set a
|
||||||
|
// Server.Handshake that does not check the origin.
|
||||||
|
type Handler func(*Conn)
|
||||||
|
|
||||||
|
func checkOrigin(config *Config, req *http.Request) (err error) {
|
||||||
|
config.Origin, err = Origin(config, req)
|
||||||
|
if err == nil && config.Origin == nil {
|
||||||
|
return fmt.Errorf("null origin")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP implements the http.Handler interface for a WebSocket
|
||||||
|
func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
s := Server{Handler: h, Handshake: checkOrigin}
|
||||||
|
s.serveWebSocket(w, req)
|
||||||
|
}
|
451
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/websocket.go
generated
vendored
Normal file
451
trunk/3rdparty/signaling/vendor/golang.org/x/net/websocket/websocket.go
generated
vendored
Normal file
|
@ -0,0 +1,451 @@
|
||||||
|
// Copyright 2009 The Go 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 a client and server for the WebSocket protocol
|
||||||
|
// as specified in RFC 6455.
|
||||||
|
//
|
||||||
|
// This package currently lacks some features found in alternative
|
||||||
|
// and more actively maintained WebSocket packages:
|
||||||
|
//
|
||||||
|
// https://godoc.org/github.com/gorilla/websocket
|
||||||
|
// https://godoc.org/nhooyr.io/websocket
|
||||||
|
package websocket // import "golang.org/x/net/websocket"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProtocolVersionHybi13 = 13
|
||||||
|
ProtocolVersionHybi = ProtocolVersionHybi13
|
||||||
|
SupportedProtocolVersion = "13"
|
||||||
|
|
||||||
|
ContinuationFrame = 0
|
||||||
|
TextFrame = 1
|
||||||
|
BinaryFrame = 2
|
||||||
|
CloseFrame = 8
|
||||||
|
PingFrame = 9
|
||||||
|
PongFrame = 10
|
||||||
|
UnknownFrame = 255
|
||||||
|
|
||||||
|
DefaultMaxPayloadBytes = 32 << 20 // 32MB
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProtocolError represents WebSocket protocol errors.
|
||||||
|
type ProtocolError struct {
|
||||||
|
ErrorString string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *ProtocolError) Error() string { return err.ErrorString }
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrBadProtocolVersion = &ProtocolError{"bad protocol version"}
|
||||||
|
ErrBadScheme = &ProtocolError{"bad scheme"}
|
||||||
|
ErrBadStatus = &ProtocolError{"bad status"}
|
||||||
|
ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"}
|
||||||
|
ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"}
|
||||||
|
ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"}
|
||||||
|
ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"}
|
||||||
|
ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"}
|
||||||
|
ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"}
|
||||||
|
ErrBadFrame = &ProtocolError{"bad frame"}
|
||||||
|
ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"}
|
||||||
|
ErrNotWebSocket = &ProtocolError{"not websocket protocol"}
|
||||||
|
ErrBadRequestMethod = &ProtocolError{"bad method"}
|
||||||
|
ErrNotSupported = &ProtocolError{"not supported"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrFrameTooLarge is returned by Codec's Receive method if payload size
|
||||||
|
// exceeds limit set by Conn.MaxPayloadBytes
|
||||||
|
var ErrFrameTooLarge = errors.New("websocket: frame payload size exceeds limit")
|
||||||
|
|
||||||
|
// Addr is an implementation of net.Addr for WebSocket.
|
||||||
|
type Addr struct {
|
||||||
|
*url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network returns the network type for a WebSocket, "websocket".
|
||||||
|
func (addr *Addr) Network() string { return "websocket" }
|
||||||
|
|
||||||
|
// Config is a WebSocket configuration
|
||||||
|
type Config struct {
|
||||||
|
// A WebSocket server address.
|
||||||
|
Location *url.URL
|
||||||
|
|
||||||
|
// A Websocket client origin.
|
||||||
|
Origin *url.URL
|
||||||
|
|
||||||
|
// WebSocket subprotocols.
|
||||||
|
Protocol []string
|
||||||
|
|
||||||
|
// WebSocket protocol version.
|
||||||
|
Version int
|
||||||
|
|
||||||
|
// TLS config for secure WebSocket (wss).
|
||||||
|
TlsConfig *tls.Config
|
||||||
|
|
||||||
|
// Additional header fields to be sent in WebSocket opening handshake.
|
||||||
|
Header http.Header
|
||||||
|
|
||||||
|
// Dialer used when opening websocket connections.
|
||||||
|
Dialer *net.Dialer
|
||||||
|
|
||||||
|
handshakeData map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverHandshaker is an interface to handle WebSocket server side handshake.
|
||||||
|
type serverHandshaker interface {
|
||||||
|
// ReadHandshake reads handshake request message from client.
|
||||||
|
// Returns http response code and error if any.
|
||||||
|
ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error)
|
||||||
|
|
||||||
|
// AcceptHandshake accepts the client handshake request and sends
|
||||||
|
// handshake response back to client.
|
||||||
|
AcceptHandshake(buf *bufio.Writer) (err error)
|
||||||
|
|
||||||
|
// NewServerConn creates a new WebSocket connection.
|
||||||
|
NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// frameReader is an interface to read a WebSocket frame.
|
||||||
|
type frameReader interface {
|
||||||
|
// Reader is to read payload of the frame.
|
||||||
|
io.Reader
|
||||||
|
|
||||||
|
// PayloadType returns payload type.
|
||||||
|
PayloadType() byte
|
||||||
|
|
||||||
|
// HeaderReader returns a reader to read header of the frame.
|
||||||
|
HeaderReader() io.Reader
|
||||||
|
|
||||||
|
// TrailerReader returns a reader to read trailer of the frame.
|
||||||
|
// If it returns nil, there is no trailer in the frame.
|
||||||
|
TrailerReader() io.Reader
|
||||||
|
|
||||||
|
// Len returns total length of the frame, including header and trailer.
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// frameReaderFactory is an interface to creates new frame reader.
|
||||||
|
type frameReaderFactory interface {
|
||||||
|
NewFrameReader() (r frameReader, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// frameWriter is an interface to write a WebSocket frame.
|
||||||
|
type frameWriter interface {
|
||||||
|
// Writer is to write payload of the frame.
|
||||||
|
io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
// frameWriterFactory is an interface to create new frame writer.
|
||||||
|
type frameWriterFactory interface {
|
||||||
|
NewFrameWriter(payloadType byte) (w frameWriter, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type frameHandler interface {
|
||||||
|
HandleFrame(frame frameReader) (r frameReader, err error)
|
||||||
|
WriteClose(status int) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn represents a WebSocket connection.
|
||||||
|
//
|
||||||
|
// Multiple goroutines may invoke methods on a Conn simultaneously.
|
||||||
|
type Conn struct {
|
||||||
|
config *Config
|
||||||
|
request *http.Request
|
||||||
|
|
||||||
|
buf *bufio.ReadWriter
|
||||||
|
rwc io.ReadWriteCloser
|
||||||
|
|
||||||
|
rio sync.Mutex
|
||||||
|
frameReaderFactory
|
||||||
|
frameReader
|
||||||
|
|
||||||
|
wio sync.Mutex
|
||||||
|
frameWriterFactory
|
||||||
|
|
||||||
|
frameHandler
|
||||||
|
PayloadType byte
|
||||||
|
defaultCloseStatus int
|
||||||
|
|
||||||
|
// MaxPayloadBytes limits the size of frame payload received over Conn
|
||||||
|
// by Codec's Receive method. If zero, DefaultMaxPayloadBytes is used.
|
||||||
|
MaxPayloadBytes int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements the io.Reader interface:
|
||||||
|
// it reads data of a frame from the WebSocket connection.
|
||||||
|
// if msg is not large enough for the frame data, it fills the msg and next Read
|
||||||
|
// will read the rest of the frame data.
|
||||||
|
// it reads Text frame or Binary frame.
|
||||||
|
func (ws *Conn) Read(msg []byte) (n int, err error) {
|
||||||
|
ws.rio.Lock()
|
||||||
|
defer ws.rio.Unlock()
|
||||||
|
again:
|
||||||
|
if ws.frameReader == nil {
|
||||||
|
frame, err := ws.frameReaderFactory.NewFrameReader()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
ws.frameReader, err = ws.frameHandler.HandleFrame(frame)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if ws.frameReader == nil {
|
||||||
|
goto again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err = ws.frameReader.Read(msg)
|
||||||
|
if err == io.EOF {
|
||||||
|
if trailer := ws.frameReader.TrailerReader(); trailer != nil {
|
||||||
|
io.Copy(ioutil.Discard, trailer)
|
||||||
|
}
|
||||||
|
ws.frameReader = nil
|
||||||
|
goto again
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implements the io.Writer interface:
|
||||||
|
// it writes data as a frame to the WebSocket connection.
|
||||||
|
func (ws *Conn) Write(msg []byte) (n int, err error) {
|
||||||
|
ws.wio.Lock()
|
||||||
|
defer ws.wio.Unlock()
|
||||||
|
w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
n, err = w.Write(msg)
|
||||||
|
w.Close()
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements the io.Closer interface.
|
||||||
|
func (ws *Conn) Close() error {
|
||||||
|
err := ws.frameHandler.WriteClose(ws.defaultCloseStatus)
|
||||||
|
err1 := ws.rwc.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClientConn reports whether ws is a client-side connection.
|
||||||
|
func (ws *Conn) IsClientConn() bool { return ws.request == nil }
|
||||||
|
|
||||||
|
// IsServerConn reports whether ws is a server-side connection.
|
||||||
|
func (ws *Conn) IsServerConn() bool { return ws.request != nil }
|
||||||
|
|
||||||
|
// LocalAddr returns the WebSocket Origin for the connection for client, or
|
||||||
|
// the WebSocket location for server.
|
||||||
|
func (ws *Conn) LocalAddr() net.Addr {
|
||||||
|
if ws.IsClientConn() {
|
||||||
|
return &Addr{ws.config.Origin}
|
||||||
|
}
|
||||||
|
return &Addr{ws.config.Location}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteAddr returns the WebSocket location for the connection for client, or
|
||||||
|
// the Websocket Origin for server.
|
||||||
|
func (ws *Conn) RemoteAddr() net.Addr {
|
||||||
|
if ws.IsClientConn() {
|
||||||
|
return &Addr{ws.config.Location}
|
||||||
|
}
|
||||||
|
return &Addr{ws.config.Origin}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
|
||||||
|
|
||||||
|
// SetDeadline sets the connection's network read & write deadlines.
|
||||||
|
func (ws *Conn) SetDeadline(t time.Time) error {
|
||||||
|
if conn, ok := ws.rwc.(net.Conn); ok {
|
||||||
|
return conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
return errSetDeadline
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline sets the connection's network read deadline.
|
||||||
|
func (ws *Conn) SetReadDeadline(t time.Time) error {
|
||||||
|
if conn, ok := ws.rwc.(net.Conn); ok {
|
||||||
|
return conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
return errSetDeadline
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline sets the connection's network write deadline.
|
||||||
|
func (ws *Conn) SetWriteDeadline(t time.Time) error {
|
||||||
|
if conn, ok := ws.rwc.(net.Conn); ok {
|
||||||
|
return conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
return errSetDeadline
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config returns the WebSocket config.
|
||||||
|
func (ws *Conn) Config() *Config { return ws.config }
|
||||||
|
|
||||||
|
// Request returns the http request upgraded to the WebSocket.
|
||||||
|
// It is nil for client side.
|
||||||
|
func (ws *Conn) Request() *http.Request { return ws.request }
|
||||||
|
|
||||||
|
// Codec represents a symmetric pair of functions that implement a codec.
|
||||||
|
type Codec struct {
|
||||||
|
Marshal func(v interface{}) (data []byte, payloadType byte, err error)
|
||||||
|
Unmarshal func(data []byte, payloadType byte, v interface{}) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send sends v marshaled by cd.Marshal as single frame to ws.
|
||||||
|
func (cd Codec) Send(ws *Conn, v interface{}) (err error) {
|
||||||
|
data, payloadType, err := cd.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ws.wio.Lock()
|
||||||
|
defer ws.wio.Unlock()
|
||||||
|
w, err := ws.frameWriterFactory.NewFrameWriter(payloadType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores
|
||||||
|
// in v. The whole frame payload is read to an in-memory buffer; max size of
|
||||||
|
// payload is defined by ws.MaxPayloadBytes. If frame payload size exceeds
|
||||||
|
// limit, ErrFrameTooLarge is returned; in this case frame is not read off wire
|
||||||
|
// completely. The next call to Receive would read and discard leftover data of
|
||||||
|
// previous oversized frame before processing next frame.
|
||||||
|
func (cd Codec) Receive(ws *Conn, v interface{}) (err error) {
|
||||||
|
ws.rio.Lock()
|
||||||
|
defer ws.rio.Unlock()
|
||||||
|
if ws.frameReader != nil {
|
||||||
|
_, err = io.Copy(ioutil.Discard, ws.frameReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ws.frameReader = nil
|
||||||
|
}
|
||||||
|
again:
|
||||||
|
frame, err := ws.frameReaderFactory.NewFrameReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
frame, err = ws.frameHandler.HandleFrame(frame)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if frame == nil {
|
||||||
|
goto again
|
||||||
|
}
|
||||||
|
maxPayloadBytes := ws.MaxPayloadBytes
|
||||||
|
if maxPayloadBytes == 0 {
|
||||||
|
maxPayloadBytes = DefaultMaxPayloadBytes
|
||||||
|
}
|
||||||
|
if hf, ok := frame.(*hybiFrameReader); ok && hf.header.Length > int64(maxPayloadBytes) {
|
||||||
|
// payload size exceeds limit, no need to call Unmarshal
|
||||||
|
//
|
||||||
|
// set frameReader to current oversized frame so that
|
||||||
|
// the next call to this function can drain leftover
|
||||||
|
// data before processing the next frame
|
||||||
|
ws.frameReader = frame
|
||||||
|
return ErrFrameTooLarge
|
||||||
|
}
|
||||||
|
payloadType := frame.PayloadType()
|
||||||
|
data, err := ioutil.ReadAll(frame)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cd.Unmarshal(data, payloadType, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
||||||
|
switch data := v.(type) {
|
||||||
|
case string:
|
||||||
|
return []byte(data), TextFrame, nil
|
||||||
|
case []byte:
|
||||||
|
return data, BinaryFrame, nil
|
||||||
|
}
|
||||||
|
return nil, UnknownFrame, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
||||||
|
switch data := v.(type) {
|
||||||
|
case *string:
|
||||||
|
*data = string(msg)
|
||||||
|
return nil
|
||||||
|
case *[]byte:
|
||||||
|
*data = msg
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Message is a codec to send/receive text/binary data in a frame on WebSocket connection.
|
||||||
|
To send/receive text frame, use string type.
|
||||||
|
To send/receive binary frame, use []byte type.
|
||||||
|
|
||||||
|
Trivial usage:
|
||||||
|
|
||||||
|
import "websocket"
|
||||||
|
|
||||||
|
// receive text frame
|
||||||
|
var message string
|
||||||
|
websocket.Message.Receive(ws, &message)
|
||||||
|
|
||||||
|
// send text frame
|
||||||
|
message = "hello"
|
||||||
|
websocket.Message.Send(ws, message)
|
||||||
|
|
||||||
|
// receive binary frame
|
||||||
|
var data []byte
|
||||||
|
websocket.Message.Receive(ws, &data)
|
||||||
|
|
||||||
|
// send binary frame
|
||||||
|
data = []byte{0, 1, 2}
|
||||||
|
websocket.Message.Send(ws, data)
|
||||||
|
|
||||||
|
*/
|
||||||
|
var Message = Codec{marshal, unmarshal}
|
||||||
|
|
||||||
|
func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
||||||
|
msg, err = json.Marshal(v)
|
||||||
|
return msg, TextFrame, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
||||||
|
return json.Unmarshal(msg, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.
|
||||||
|
|
||||||
|
Trivial usage:
|
||||||
|
|
||||||
|
import "websocket"
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
Msg string
|
||||||
|
Count int
|
||||||
|
}
|
||||||
|
|
||||||
|
// receive JSON type T
|
||||||
|
var data T
|
||||||
|
websocket.JSON.Receive(ws, &data)
|
||||||
|
|
||||||
|
// send JSON type T
|
||||||
|
websocket.JSON.Send(ws, data)
|
||||||
|
*/
|
||||||
|
var JSON = Codec{jsonMarshal, jsonUnmarshal}
|
7
trunk/3rdparty/signaling/vendor/modules.txt
vendored
Normal file
7
trunk/3rdparty/signaling/vendor/modules.txt
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# github.com/ossrs/go-oryx-lib v0.0.8
|
||||||
|
## explicit
|
||||||
|
github.com/ossrs/go-oryx-lib/errors
|
||||||
|
github.com/ossrs/go-oryx-lib/logger
|
||||||
|
# golang.org/x/net v0.0.0-20210502030024-e5908800b52b
|
||||||
|
## explicit
|
||||||
|
golang.org/x/net/websocket
|
3
trunk/3rdparty/signaling/www/crossdomain.xml
vendored
Normal file
3
trunk/3rdparty/signaling/www/crossdomain.xml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<cross-domain-policy>
|
||||||
|
<allow-access-from domain="*"/>
|
||||||
|
</cross-domain-policy>
|
9
trunk/3rdparty/signaling/www/demos/css/bootstrap.min.css
vendored
Executable file
9
trunk/3rdparty/signaling/www/demos/css/bootstrap.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
BIN
trunk/3rdparty/signaling/www/demos/img/tooltip.png
vendored
Normal file
BIN
trunk/3rdparty/signaling/www/demos/img/tooltip.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 783 B |
13
trunk/3rdparty/signaling/www/demos/index.html
vendored
Normal file
13
trunk/3rdparty/signaling/www/demos/index.html
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>SRS</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h3><a href="https://github.com/ossrs/signaling">Signaling</a> works!</h3>
|
||||||
|
<p>
|
||||||
|
Run demo for <a href="one2one.html">WebRTC: One to One over SFU(SRS)</a><br/>
|
||||||
|
点击进入<a href="one2one.html">SRS一对一通话演示</a>
|
||||||
|
</p>
|
||||||
|
</body>
|
5551
trunk/3rdparty/signaling/www/demos/js/adapter-7.4.0.js
vendored
Normal file
5551
trunk/3rdparty/signaling/www/demos/js/adapter-7.4.0.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
trunk/3rdparty/signaling/www/demos/js/adapter-7.4.0.min.js
vendored
Normal file
1
trunk/3rdparty/signaling/www/demos/js/adapter-7.4.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
trunk/3rdparty/signaling/www/demos/js/jquery-1.10.2.min.js
vendored
Normal file
6
trunk/3rdparty/signaling/www/demos/js/jquery-1.10.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
trunk/3rdparty/signaling/www/demos/js/jquery-1.10.2.min.map
vendored
Normal file
1
trunk/3rdparty/signaling/www/demos/js/jquery-1.10.2.min.map
vendored
Normal file
File diff suppressed because one or more lines are too long
509
trunk/3rdparty/signaling/www/demos/js/srs.sdk.js
vendored
Normal file
509
trunk/3rdparty/signaling/www/demos/js/srs.sdk.js
vendored
Normal file
|
@ -0,0 +1,509 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2021 Winlin
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
|
||||||
|
// Async-awat-prmise based SRS RTC Publisher.
|
||||||
|
function SrsRtcPublisherAsync() {
|
||||||
|
var self = {};
|
||||||
|
|
||||||
|
// @see https://github.com/rtcdn/rtcdn-draft
|
||||||
|
// @url The WebRTC url to play with, for example:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream
|
||||||
|
// or specifies the API port:
|
||||||
|
// webrtc://r.ossrs.net:11985/live/livestream
|
||||||
|
// or autostart the publish:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?autostart=true
|
||||||
|
// or change the app from live to myapp:
|
||||||
|
// webrtc://r.ossrs.net:11985/myapp/livestream
|
||||||
|
// or change the stream from livestream to mystream:
|
||||||
|
// webrtc://r.ossrs.net:11985/live/mystream
|
||||||
|
// or set the api server to myapi.domain.com:
|
||||||
|
// webrtc://myapi.domain.com/live/livestream
|
||||||
|
// or set the candidate(ip) of answer:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
|
||||||
|
// or force to access https API:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?schema=https
|
||||||
|
// or use plaintext, without SRTP:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?encrypt=false
|
||||||
|
// or any other information, will pass-by in the query:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?token=xxx
|
||||||
|
self.publish = async function (url) {
|
||||||
|
var conf = self.__internal.prepareUrl(url);
|
||||||
|
self.pc.addTransceiver("audio", {direction: "sendonly"});
|
||||||
|
self.pc.addTransceiver("video", {direction: "sendonly"});
|
||||||
|
|
||||||
|
var stream = await navigator.mediaDevices.getUserMedia(
|
||||||
|
{audio: true, video: {height: {max: 320}}}
|
||||||
|
);
|
||||||
|
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
|
||||||
|
stream.getTracks().forEach(function (track) {
|
||||||
|
self.pc.addTrack(track);
|
||||||
|
});
|
||||||
|
|
||||||
|
var offer = await self.pc.createOffer();
|
||||||
|
await self.pc.setLocalDescription(offer);
|
||||||
|
var session = await new Promise(function (resolve, reject) {
|
||||||
|
// @see https://github.com/rtcdn/rtcdn-draft
|
||||||
|
var data = {
|
||||||
|
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
|
||||||
|
clientip: null, sdp: offer.sdp
|
||||||
|
};
|
||||||
|
console.log("Generated offer: ", data);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
|
||||||
|
contentType: 'application/json', dataType: 'json'
|
||||||
|
}).done(function (data) {
|
||||||
|
console.log("Got answer: ", data);
|
||||||
|
if (data.code) {
|
||||||
|
reject(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data);
|
||||||
|
}).fail(function (reason) {
|
||||||
|
reject(reason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await self.pc.setRemoteDescription(
|
||||||
|
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
|
||||||
|
);
|
||||||
|
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
|
||||||
|
|
||||||
|
// Notify about local stream when success.
|
||||||
|
self.onaddstream && self.onaddstream({stream: stream});
|
||||||
|
|
||||||
|
return session;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Close the publisher.
|
||||||
|
self.close = function () {
|
||||||
|
self.pc && self.pc.close();
|
||||||
|
self.pc = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The callback when got local stream.
|
||||||
|
self.onaddstream = function (event) {
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal APIs.
|
||||||
|
self.__internal = {
|
||||||
|
defaultPath: '/rtc/v1/publish/',
|
||||||
|
prepareUrl: function (webrtcUrl) {
|
||||||
|
var urlObject = self.__internal.parse(webrtcUrl);
|
||||||
|
|
||||||
|
// If user specifies the schema, use it as API schema.
|
||||||
|
var schema = urlObject.user_query.schema;
|
||||||
|
schema = schema ? schema + ':' : window.location.protocol;
|
||||||
|
|
||||||
|
var port = urlObject.port || 1985;
|
||||||
|
if (schema === 'https:') {
|
||||||
|
port = urlObject.port || 443;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @see https://github.com/rtcdn/rtcdn-draft
|
||||||
|
var api = urlObject.user_query.play || self.__internal.defaultPath;
|
||||||
|
if (api.lastIndexOf('/') !== api.length - 1) {
|
||||||
|
api += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
|
||||||
|
for (var key in urlObject.user_query) {
|
||||||
|
if (key !== 'api' && key !== 'play') {
|
||||||
|
apiUrl += '&' + key + '=' + urlObject.user_query[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
|
||||||
|
var apiUrl = apiUrl.replace(api + '&', api + '?');
|
||||||
|
|
||||||
|
var streamUrl = urlObject.url;
|
||||||
|
|
||||||
|
return {
|
||||||
|
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
|
||||||
|
tid: new Date().getTime().toString(16)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
parse: function (url) {
|
||||||
|
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||||
|
var a = document.createElement("a");
|
||||||
|
a.href = url.replace("rtmp://", "http://")
|
||||||
|
.replace("webrtc://", "http://")
|
||||||
|
.replace("rtc://", "http://");
|
||||||
|
|
||||||
|
var vhost = a.hostname;
|
||||||
|
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
|
||||||
|
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
|
||||||
|
|
||||||
|
// parse the vhost in the params of app, that srs supports.
|
||||||
|
app = app.replace("...vhost...", "?vhost=");
|
||||||
|
if (app.indexOf("?") >= 0) {
|
||||||
|
var params = app.substr(app.indexOf("?"));
|
||||||
|
app = app.substr(0, app.indexOf("?"));
|
||||||
|
|
||||||
|
if (params.indexOf("vhost=") > 0) {
|
||||||
|
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
|
||||||
|
if (vhost.indexOf("&") > 0) {
|
||||||
|
vhost = vhost.substr(0, vhost.indexOf("&"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// when vhost equals to server, and server is ip,
|
||||||
|
// the vhost is __defaultVhost__
|
||||||
|
if (a.hostname === vhost) {
|
||||||
|
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
|
||||||
|
if (re.test(a.hostname)) {
|
||||||
|
vhost = "__defaultVhost__";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the schema
|
||||||
|
var schema = "rtmp";
|
||||||
|
if (url.indexOf("://") > 0) {
|
||||||
|
schema = url.substr(0, url.indexOf("://"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var port = a.port;
|
||||||
|
if (!port) {
|
||||||
|
if (schema === 'http') {
|
||||||
|
port = 80;
|
||||||
|
} else if (schema === 'https') {
|
||||||
|
port = 443;
|
||||||
|
} else if (schema === 'rtmp') {
|
||||||
|
port = 1935;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = {
|
||||||
|
url: url,
|
||||||
|
schema: schema,
|
||||||
|
server: a.hostname, port: port,
|
||||||
|
vhost: vhost, app: app, stream: stream
|
||||||
|
};
|
||||||
|
self.__internal.fill_query(a.search, ret);
|
||||||
|
|
||||||
|
// For webrtc API, we use 443 if page is https, or schema specified it.
|
||||||
|
if (!ret.port) {
|
||||||
|
if (schema === 'webrtc' || schema === 'rtc') {
|
||||||
|
if (ret.user_query.schema === 'https') {
|
||||||
|
ret.port = 443;
|
||||||
|
} else if (window.location.href.indexOf('https://') === 0) {
|
||||||
|
ret.port = 443;
|
||||||
|
} else {
|
||||||
|
// For WebRTC, SRS use 1985 as default API port.
|
||||||
|
ret.port = 1985;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
fill_query: function (query_string, obj) {
|
||||||
|
// pure user query object.
|
||||||
|
obj.user_query = {};
|
||||||
|
|
||||||
|
if (query_string.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split again for angularjs.
|
||||||
|
if (query_string.indexOf("?") >= 0) {
|
||||||
|
query_string = query_string.split("?")[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var queries = query_string.split("&");
|
||||||
|
for (var i = 0; i < queries.length; i++) {
|
||||||
|
var elem = queries[i];
|
||||||
|
|
||||||
|
var query = elem.split("=");
|
||||||
|
obj[query[0]] = query[1];
|
||||||
|
obj.user_query[query[0]] = query[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// alias domain for vhost.
|
||||||
|
if (obj.domain) {
|
||||||
|
obj.vhost = obj.domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.pc = new RTCPeerConnection(null);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
|
||||||
|
// Async-await-promise based SRS RTC Player.
|
||||||
|
function SrsRtcPlayerAsync() {
|
||||||
|
var self = {};
|
||||||
|
|
||||||
|
// @see https://github.com/rtcdn/rtcdn-draft
|
||||||
|
// @url The WebRTC url to play with, for example:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream
|
||||||
|
// or specifies the API port:
|
||||||
|
// webrtc://r.ossrs.net:11985/live/livestream
|
||||||
|
// or autostart the play:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?autostart=true
|
||||||
|
// or change the app from live to myapp:
|
||||||
|
// webrtc://r.ossrs.net:11985/myapp/livestream
|
||||||
|
// or change the stream from livestream to mystream:
|
||||||
|
// webrtc://r.ossrs.net:11985/live/mystream
|
||||||
|
// or set the api server to myapi.domain.com:
|
||||||
|
// webrtc://myapi.domain.com/live/livestream
|
||||||
|
// or set the candidate(ip) of answer:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
|
||||||
|
// or force to access https API:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?schema=https
|
||||||
|
// or use plaintext, without SRTP:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?encrypt=false
|
||||||
|
// or any other information, will pass-by in the query:
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
|
||||||
|
// webrtc://r.ossrs.net/live/livestream?token=xxx
|
||||||
|
self.play = async function(url) {
|
||||||
|
var conf = self.__internal.prepareUrl(url);
|
||||||
|
self.pc.addTransceiver("audio", {direction: "recvonly"});
|
||||||
|
self.pc.addTransceiver("video", {direction: "recvonly"});
|
||||||
|
|
||||||
|
var offer = await self.pc.createOffer();
|
||||||
|
await self.pc.setLocalDescription(offer);
|
||||||
|
var session = await new Promise(function(resolve, reject) {
|
||||||
|
// @see https://github.com/rtcdn/rtcdn-draft
|
||||||
|
var data = {
|
||||||
|
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
|
||||||
|
clientip: null, sdp: offer.sdp
|
||||||
|
};
|
||||||
|
console.log("Generated offer: ", data);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
|
||||||
|
contentType:'application/json', dataType: 'json'
|
||||||
|
}).done(function(data) {
|
||||||
|
console.log("Got answer: ", data);
|
||||||
|
if (data.code) {
|
||||||
|
reject(data); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(data);
|
||||||
|
}).fail(function(reason){
|
||||||
|
reject(reason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await self.pc.setRemoteDescription(
|
||||||
|
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
|
||||||
|
);
|
||||||
|
return session;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Close the player.
|
||||||
|
self.close = function() {
|
||||||
|
self.pc && self.pc.close();
|
||||||
|
self.pc = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The callback when got remote stream.
|
||||||
|
self.onaddstream = function (event) {};
|
||||||
|
|
||||||
|
// Internal APIs.
|
||||||
|
self.__internal = {
|
||||||
|
defaultPath: '/rtc/v1/play/',
|
||||||
|
prepareUrl: function (webrtcUrl) {
|
||||||
|
var urlObject = self.__internal.parse(webrtcUrl);
|
||||||
|
|
||||||
|
// If user specifies the schema, use it as API schema.
|
||||||
|
var schema = urlObject.user_query.schema;
|
||||||
|
schema = schema ? schema + ':' : window.location.protocol;
|
||||||
|
|
||||||
|
var port = urlObject.port || 1985;
|
||||||
|
if (schema === 'https:') {
|
||||||
|
port = urlObject.port || 443;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @see https://github.com/rtcdn/rtcdn-draft
|
||||||
|
var api = urlObject.user_query.play || self.__internal.defaultPath;
|
||||||
|
if (api.lastIndexOf('/') !== api.length - 1) {
|
||||||
|
api += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
|
||||||
|
for (var key in urlObject.user_query) {
|
||||||
|
if (key !== 'api' && key !== 'play') {
|
||||||
|
apiUrl += '&' + key + '=' + urlObject.user_query[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
|
||||||
|
var apiUrl = apiUrl.replace(api + '&', api + '?');
|
||||||
|
|
||||||
|
var streamUrl = urlObject.url;
|
||||||
|
|
||||||
|
return {
|
||||||
|
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
|
||||||
|
tid: new Date().getTime().toString(16)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
parse: function (url) {
|
||||||
|
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||||
|
var a = document.createElement("a");
|
||||||
|
a.href = url.replace("rtmp://", "http://")
|
||||||
|
.replace("webrtc://", "http://")
|
||||||
|
.replace("rtc://", "http://");
|
||||||
|
|
||||||
|
var vhost = a.hostname;
|
||||||
|
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
|
||||||
|
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
|
||||||
|
|
||||||
|
// parse the vhost in the params of app, that srs supports.
|
||||||
|
app = app.replace("...vhost...", "?vhost=");
|
||||||
|
if (app.indexOf("?") >= 0) {
|
||||||
|
var params = app.substr(app.indexOf("?"));
|
||||||
|
app = app.substr(0, app.indexOf("?"));
|
||||||
|
|
||||||
|
if (params.indexOf("vhost=") > 0) {
|
||||||
|
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
|
||||||
|
if (vhost.indexOf("&") > 0) {
|
||||||
|
vhost = vhost.substr(0, vhost.indexOf("&"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// when vhost equals to server, and server is ip,
|
||||||
|
// the vhost is __defaultVhost__
|
||||||
|
if (a.hostname === vhost) {
|
||||||
|
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
|
||||||
|
if (re.test(a.hostname)) {
|
||||||
|
vhost = "__defaultVhost__";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the schema
|
||||||
|
var schema = "rtmp";
|
||||||
|
if (url.indexOf("://") > 0) {
|
||||||
|
schema = url.substr(0, url.indexOf("://"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var port = a.port;
|
||||||
|
if (!port) {
|
||||||
|
if (schema === 'http') {
|
||||||
|
port = 80;
|
||||||
|
} else if (schema === 'https') {
|
||||||
|
port = 443;
|
||||||
|
} else if (schema === 'rtmp') {
|
||||||
|
port = 1935;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = {
|
||||||
|
url: url,
|
||||||
|
schema: schema,
|
||||||
|
server: a.hostname, port: port,
|
||||||
|
vhost: vhost, app: app, stream: stream
|
||||||
|
};
|
||||||
|
self.__internal.fill_query(a.search, ret);
|
||||||
|
|
||||||
|
// For webrtc API, we use 443 if page is https, or schema specified it.
|
||||||
|
if (!ret.port) {
|
||||||
|
if (schema === 'webrtc' || schema === 'rtc') {
|
||||||
|
if (ret.user_query.schema === 'https') {
|
||||||
|
ret.port = 443;
|
||||||
|
} else if (window.location.href.indexOf('https://') === 0) {
|
||||||
|
ret.port = 443;
|
||||||
|
} else {
|
||||||
|
// For WebRTC, SRS use 1985 as default API port.
|
||||||
|
ret.port = 1985;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
fill_query: function (query_string, obj) {
|
||||||
|
// pure user query object.
|
||||||
|
obj.user_query = {};
|
||||||
|
|
||||||
|
if (query_string.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split again for angularjs.
|
||||||
|
if (query_string.indexOf("?") >= 0) {
|
||||||
|
query_string = query_string.split("?")[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var queries = query_string.split("&");
|
||||||
|
for (var i = 0; i < queries.length; i++) {
|
||||||
|
var elem = queries[i];
|
||||||
|
|
||||||
|
var query = elem.split("=");
|
||||||
|
obj[query[0]] = query[1];
|
||||||
|
obj.user_query[query[0]] = query[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// alias domain for vhost.
|
||||||
|
if (obj.domain) {
|
||||||
|
obj.vhost = obj.domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.pc = new RTCPeerConnection(null);
|
||||||
|
self.pc.onaddstream = function (event) {
|
||||||
|
if (self.onaddstream) {
|
||||||
|
self.onaddstream(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
|
||||||
|
function SrsRtcFormatSenders(senders, kind) {
|
||||||
|
var codecs = [];
|
||||||
|
senders.forEach(function (sender) {
|
||||||
|
sender.getParameters().codecs.forEach(function(c) {
|
||||||
|
if (kind && sender.track.kind !== kind) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = '';
|
||||||
|
|
||||||
|
s += c.mimeType.replace('audio/', '').replace('video/', '');
|
||||||
|
s += ', ' + c.clockRate + 'HZ';
|
||||||
|
if (sender.track.kind === "audio") {
|
||||||
|
s += ', channels: ' + c.channels;
|
||||||
|
}
|
||||||
|
s += ', pt: ' + c.payloadType;
|
||||||
|
|
||||||
|
codecs.push(s);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return codecs.join(", ");
|
||||||
|
}
|
||||||
|
|
88
trunk/3rdparty/signaling/www/demos/js/srs.sig.js
vendored
Normal file
88
trunk/3rdparty/signaling/www/demos/js/srs.sig.js
vendored
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2021 Winlin
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Async-await-promise based SRS RTC Signaling.
|
||||||
|
function SrsRtcSignalingAsync() {
|
||||||
|
var self = {};
|
||||||
|
|
||||||
|
// The schema is ws or wss, host is ip or ip:port, display is nickname
|
||||||
|
// of user to join the room.
|
||||||
|
self.connect = async function (schema, host, room, display) {
|
||||||
|
var url = schema + '://' + host + '/sig/v1/rtc';
|
||||||
|
self.ws = new WebSocket(url + '?room=' + room + '&display=' + display);
|
||||||
|
|
||||||
|
self.ws.onmessage = function(event) {
|
||||||
|
var r = JSON.parse(event.data);
|
||||||
|
var promise = self._internals.msgs[r.tid];
|
||||||
|
if (promise) {
|
||||||
|
promise.resolve(r.msg);
|
||||||
|
delete self._internals.msgs[r.tid];
|
||||||
|
} else {
|
||||||
|
self.onmessage(r.msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
self.ws.onopen = function (event) {
|
||||||
|
resolve(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.ws.onerror = function (event) {
|
||||||
|
reject(event);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// The message is a json object.
|
||||||
|
self.send = async function (message) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var r = {tid: new Date().getTime().toString(16), msg: message};
|
||||||
|
self._internals.msgs[r.tid] = {resolve: resolve, reject: reject};
|
||||||
|
self.ws.send(JSON.stringify(r));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.close = function () {
|
||||||
|
self.ws && self.ws.close();
|
||||||
|
self.ws = null;
|
||||||
|
|
||||||
|
for (const tid in self._internals.msgs) {
|
||||||
|
var promise = self._internals.msgs[tid];
|
||||||
|
promise.reject('close');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The callback when got messages from signaling server.
|
||||||
|
self.onmessage = function (msg) {
|
||||||
|
};
|
||||||
|
|
||||||
|
self._internals = {
|
||||||
|
// Key is tid, value is object {resolve, reject, response}.
|
||||||
|
msgs: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
185
trunk/3rdparty/signaling/www/demos/one2one.html
vendored
Normal file
185
trunk/3rdparty/signaling/www/demos/one2one.html
vendored
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>SRS</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style>
|
||||||
|
body{
|
||||||
|
padding-top: 55px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||||
|
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/adapter-7.4.0.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/srs.sdk.js"></script>
|
||||||
|
<script type="text/javascript" src="js/srs.sig.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/rtcpublisher'/>
|
||||||
|
<div class="navbar navbar-fixed-top">
|
||||||
|
<div class="navbar-inner">
|
||||||
|
<div class="container">
|
||||||
|
<a class="brand" href="https://github.com/ossrs/srs">SRS</a>
|
||||||
|
<div class="nav-collapse collapse">
|
||||||
|
<ul class="nav">
|
||||||
|
<li class="active"><a href="#">一对一通话</a></li>
|
||||||
|
<li>
|
||||||
|
<a href="https://github.com/ossrs/signaling">
|
||||||
|
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ossrs/signaling?style=social">
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="form-inline">
|
||||||
|
SRS:
|
||||||
|
<input type="text" id="txt_host" class="input-medium" value="">
|
||||||
|
Room:
|
||||||
|
<input type="text" id="txt_room" class="input-small" value="live">
|
||||||
|
Display:
|
||||||
|
<input type="text" id="txt_display" class="input-small" value="">
|
||||||
|
<button class="btn btn-primary" id="btn_start">开始通话</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="span5">
|
||||||
|
<label></label>
|
||||||
|
<video id="rtc_media_publisher" width="320" autoplay muted controls></video>
|
||||||
|
|
||||||
|
<label></label>
|
||||||
|
<span id='self'></span>
|
||||||
|
</div>
|
||||||
|
<div class="span6">
|
||||||
|
<label></label>
|
||||||
|
<video id="rtc_media_player" width="320" autoplay muted controls></video>
|
||||||
|
|
||||||
|
<label></label>
|
||||||
|
<span id='peer'></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var sig = null;
|
||||||
|
var publisher = null;
|
||||||
|
var player = null;
|
||||||
|
$(function(){
|
||||||
|
console.log('?host=x to specify the SRS server');
|
||||||
|
console.log('?room=x to specify the room to join');
|
||||||
|
console.log('?display=x to specify your nick name');
|
||||||
|
|
||||||
|
var startDemo = async function () {
|
||||||
|
var host = $('#txt_host').val();
|
||||||
|
var room = $('#txt_room').val();
|
||||||
|
var display = $('#txt_display').val();
|
||||||
|
|
||||||
|
// Connect to signaling first.
|
||||||
|
if (sig) {
|
||||||
|
sig.close();
|
||||||
|
}
|
||||||
|
sig = new SrsRtcSignalingAsync();
|
||||||
|
sig.onmessage = function (msg) {
|
||||||
|
console.log('Notify: ', msg);
|
||||||
|
msg.participants.forEach(function (participant) {
|
||||||
|
if (participant.display === display || !participant.publishing) return;
|
||||||
|
startPlay(host, room, participant.display);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
await sig.connect('ws', window.location.host, room, display);
|
||||||
|
|
||||||
|
let r0 = await sig.send({action:'join', room:room, display:display});
|
||||||
|
console.log('Signaling: join ok', r0);
|
||||||
|
|
||||||
|
// For one to one demo, alert and ignore when room is full.
|
||||||
|
if (r0.participants.length > 2) {
|
||||||
|
alert('Room is full, already ' + (r0.participants.length - 1) + ' participants');
|
||||||
|
sig.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start publish media if signaling is ok.
|
||||||
|
await startPublish(host, room, display);
|
||||||
|
let r1 = await sig.send({action:'publish', room:room, display:display});
|
||||||
|
console.log('Signaling: publish ok', r1);
|
||||||
|
|
||||||
|
// Play the stream already in room.
|
||||||
|
r0.participants.forEach(function(participant) {
|
||||||
|
if (participant.display === display || !participant.publishing) return;
|
||||||
|
startPlay(host, room, participant.display);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var startPublish = function (host, room, display) {
|
||||||
|
var url = 'webrtc://' + host + '/' + room + '/' + display + query;
|
||||||
|
$('#rtc_media_publisher').show();
|
||||||
|
|
||||||
|
if (publisher) {
|
||||||
|
publisher.close();
|
||||||
|
}
|
||||||
|
publisher = new SrsRtcPublisherAsync();
|
||||||
|
publisher.onaddstream = function (event) {
|
||||||
|
console.log('Start publish, event: ', event);
|
||||||
|
$('#rtc_media_publisher').prop('srcObject', event.stream);
|
||||||
|
};
|
||||||
|
|
||||||
|
return publisher.publish(url).then(function(session){
|
||||||
|
$('#self').text('Self: ' + display);
|
||||||
|
}).catch(function (reason) {
|
||||||
|
publisher.close();
|
||||||
|
$('#rtc_media_publisher').hide();
|
||||||
|
console.error(reason);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var startPlay = function (host, room, display) {
|
||||||
|
var url = 'webrtc://' + host + '/' + room + '/' + display + query;
|
||||||
|
$('#rtc_media_player').show();
|
||||||
|
|
||||||
|
if (player) {
|
||||||
|
player.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
player = new SrsRtcPlayerAsync();
|
||||||
|
player.onaddstream = function (event) {
|
||||||
|
console.log('Start play, event: ', event);
|
||||||
|
$('#rtc_media_player').prop('srcObject', event.stream);
|
||||||
|
};
|
||||||
|
|
||||||
|
player.play(url).then(function(session){
|
||||||
|
$('#peer').text('Peer: ' + display);
|
||||||
|
$('#rtc_media_player').prop('muted', false);
|
||||||
|
}).catch(function (reason) {
|
||||||
|
player.close();
|
||||||
|
$('#rtc_media_player').hide();
|
||||||
|
console.error(reason);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#rtc_media_publisher').hide();
|
||||||
|
$('#rtc_media_player').hide();
|
||||||
|
$("#btn_start").click(startDemo);
|
||||||
|
|
||||||
|
// Pass-by to SRS url.
|
||||||
|
let query = window.location.href.split('?')[1];
|
||||||
|
query = query? '?' + query : null;
|
||||||
|
|
||||||
|
let host = window.location.href.split('host=')[1];
|
||||||
|
$('#txt_host').val(host? host.split('&')[0] : window.location.hostname);
|
||||||
|
|
||||||
|
let room = window.location.href.split('room=')[1];
|
||||||
|
room && $('#txt_room').val(room.split('&')[0]);
|
||||||
|
|
||||||
|
let display = window.location.href.split('display=')[1];
|
||||||
|
$('#txt_display').val(display? display.split('&')[0] : new Date().getTime().toString(16).substr(3));
|
||||||
|
|
||||||
|
let autostart = window.location.href.split('autostart=')[1];
|
||||||
|
if (autostart && autostart.split('&')[0] === 'true') {
|
||||||
|
startDemo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
BIN
trunk/3rdparty/signaling/www/favicon.ico
vendored
Normal file
BIN
trunk/3rdparty/signaling/www/favicon.ico
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
9
trunk/3rdparty/signaling/www/index.html
vendored
Executable file
9
trunk/3rdparty/signaling/www/index.html
vendored
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>SRS</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.location.href = "demos/"
|
||||||
|
</script>
|
||||||
|
</head>
|
|
@ -119,6 +119,11 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
||||||
api = prop->to_str();
|
api = prop->to_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string tid;
|
||||||
|
if ((prop = req->ensure_property_string("tid")) != NULL) {
|
||||||
|
tid = prop->to_str();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: FIXME: Parse vhost.
|
// TODO: FIXME: Parse vhost.
|
||||||
// Parse app and stream from streamurl.
|
// Parse app and stream from streamurl.
|
||||||
string app;
|
string app;
|
||||||
|
@ -139,9 +144,9 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
||||||
string srtp = r->query_get("encrypt");
|
string srtp = r->query_get("encrypt");
|
||||||
string dtls = r->query_get("dtls");
|
string dtls = r->query_get("dtls");
|
||||||
|
|
||||||
srs_trace("RTC play %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s, srtp=%s, dtls=%s",
|
srs_trace("RTC play %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s, srtp=%s, dtls=%s",
|
||||||
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str(),
|
streamurl.c_str(), api.c_str(), tid.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(),
|
||||||
codec.c_str(), srtp.c_str(), dtls.c_str()
|
eip.c_str(), codec.c_str(), srtp.c_str(), dtls.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
// The RTC user config object.
|
// The RTC user config object.
|
||||||
|
@ -488,6 +493,11 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHtt
|
||||||
api = prop->to_str();
|
api = prop->to_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string tid;
|
||||||
|
if ((prop = req->ensure_property_string("tid")) != NULL) {
|
||||||
|
tid = prop->to_str();
|
||||||
|
}
|
||||||
|
|
||||||
// Parse app and stream from streamurl.
|
// Parse app and stream from streamurl.
|
||||||
string app;
|
string app;
|
||||||
string stream_name;
|
string stream_name;
|
||||||
|
@ -504,9 +514,9 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHtt
|
||||||
string eip = r->query_get("eip");
|
string eip = r->query_get("eip");
|
||||||
string codec = r->query_get("codec");
|
string codec = r->query_get("codec");
|
||||||
|
|
||||||
srs_trace("RTC publish %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s",
|
srs_trace("RTC publish %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s",
|
||||||
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str(),
|
streamurl.c_str(), api.c_str(), tid.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(),
|
||||||
codec.c_str()
|
remote_sdp_str.length(), eip.c_str(), codec.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
// The RTC user config object.
|
// The RTC user config object.
|
||||||
|
|
|
@ -26,6 +26,6 @@
|
||||||
|
|
||||||
#define VERSION_MAJOR 4
|
#define VERSION_MAJOR 4
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 95
|
#define VERSION_REVISION 96
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue