mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
01. Support GB config as StreamCaster. 02. Support disable GB by --gb28181=off. 03. Add utests for SIP examples. 04. Wireshark plugin to decode TCP/9000 as rtp.rfc4571 05. Support MPEGPS program stream codec. 06. Add utest for PS stream codec. 07. Decode MPEGPS packet stream. 08. Carry RTP and PS packet as helper in PS message. 09. Support recover from error mode. 10. Support process by a pack of PS/TS messages. 11. Add statistic for recovered and msgs dropped. 12. Recover from err position fastly. 13. Define state machine for GB session. 14. Bind context to GB session. 15. Re-invite when media disconnected. 16. Update GitHub actions with GB28181. 17. Support parse CANDIDATE by env or pip. 18. Support mux GB28181 to RTMP. 19. Support regression test by srs-bench.
331 lines
7.7 KiB
Go
331 lines
7.7 KiB
Go
// Package httphead contains utils for parsing HTTP and HTTP-grammar compatible
|
|
// text protocols headers.
|
|
//
|
|
// That is, this package first aim is to bring ability to easily parse
|
|
// constructions, described here https://tools.ietf.org/html/rfc2616#section-2
|
|
package httphead
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
)
|
|
|
|
// ScanTokens parses data in this form:
|
|
//
|
|
// list = 1#token
|
|
//
|
|
// It returns false if data is malformed.
|
|
func ScanTokens(data []byte, it func([]byte) bool) bool {
|
|
lexer := &Scanner{data: data}
|
|
|
|
var ok bool
|
|
for lexer.Next() {
|
|
switch lexer.Type() {
|
|
case ItemToken:
|
|
ok = true
|
|
if !it(lexer.Bytes()) {
|
|
return true
|
|
}
|
|
case ItemSeparator:
|
|
if !isComma(lexer.Bytes()) {
|
|
return false
|
|
}
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
return ok && !lexer.err
|
|
}
|
|
|
|
// ParseOptions parses all header options and appends it to given slice of
|
|
// Option. It returns flag of successful (wellformed input) parsing.
|
|
//
|
|
// Note that appended options are all consist of subslices of data. That is,
|
|
// mutation of data will mutate appended options.
|
|
func ParseOptions(data []byte, options []Option) ([]Option, bool) {
|
|
var i int
|
|
index := -1
|
|
return options, ScanOptions(data, func(idx int, name, attr, val []byte) Control {
|
|
if idx != index {
|
|
index = idx
|
|
i = len(options)
|
|
options = append(options, Option{Name: name})
|
|
}
|
|
if attr != nil {
|
|
options[i].Parameters.Set(attr, val)
|
|
}
|
|
return ControlContinue
|
|
})
|
|
}
|
|
|
|
// SelectFlag encodes way of options selection.
|
|
type SelectFlag byte
|
|
|
|
// String represetns flag as string.
|
|
func (f SelectFlag) String() string {
|
|
var flags [2]string
|
|
var n int
|
|
if f&SelectCopy != 0 {
|
|
flags[n] = "copy"
|
|
n++
|
|
}
|
|
if f&SelectUnique != 0 {
|
|
flags[n] = "unique"
|
|
n++
|
|
}
|
|
return "[" + strings.Join(flags[:n], "|") + "]"
|
|
}
|
|
|
|
const (
|
|
// SelectCopy causes selector to copy selected option before appending it
|
|
// to resulting slice.
|
|
// If SelectCopy flag is not passed to selector, then appended options will
|
|
// contain sub-slices of the initial data.
|
|
SelectCopy SelectFlag = 1 << iota
|
|
|
|
// SelectUnique causes selector to append only not yet existing option to
|
|
// resulting slice. Unique is checked by comparing option names.
|
|
SelectUnique
|
|
)
|
|
|
|
// OptionSelector contains configuration for selecting Options from header value.
|
|
type OptionSelector struct {
|
|
// Check is a filter function that applied to every Option that possibly
|
|
// could be selected.
|
|
// If Check is nil all options will be selected.
|
|
Check func(Option) bool
|
|
|
|
// Flags contains flags for options selection.
|
|
Flags SelectFlag
|
|
|
|
// Alloc used to allocate slice of bytes when selector is configured with
|
|
// SelectCopy flag. It will be called with number of bytes needed for copy
|
|
// of single Option.
|
|
// If Alloc is nil make is used.
|
|
Alloc func(n int) []byte
|
|
}
|
|
|
|
// Select parses header data and appends it to given slice of Option.
|
|
// It also returns flag of successful (wellformed input) parsing.
|
|
func (s OptionSelector) Select(data []byte, options []Option) ([]Option, bool) {
|
|
var current Option
|
|
var has bool
|
|
index := -1
|
|
|
|
alloc := s.Alloc
|
|
if alloc == nil {
|
|
alloc = defaultAlloc
|
|
}
|
|
check := s.Check
|
|
if check == nil {
|
|
check = defaultCheck
|
|
}
|
|
|
|
ok := ScanOptions(data, func(idx int, name, attr, val []byte) Control {
|
|
if idx != index {
|
|
if has && check(current) {
|
|
if s.Flags&SelectCopy != 0 {
|
|
current = current.Copy(alloc(current.Size()))
|
|
}
|
|
options = append(options, current)
|
|
has = false
|
|
}
|
|
if s.Flags&SelectUnique != 0 {
|
|
for i := len(options) - 1; i >= 0; i-- {
|
|
if bytes.Equal(options[i].Name, name) {
|
|
return ControlSkip
|
|
}
|
|
}
|
|
}
|
|
index = idx
|
|
current = Option{Name: name}
|
|
has = true
|
|
}
|
|
if attr != nil {
|
|
current.Parameters.Set(attr, val)
|
|
}
|
|
|
|
return ControlContinue
|
|
})
|
|
if has && check(current) {
|
|
if s.Flags&SelectCopy != 0 {
|
|
current = current.Copy(alloc(current.Size()))
|
|
}
|
|
options = append(options, current)
|
|
}
|
|
|
|
return options, ok
|
|
}
|
|
|
|
func defaultAlloc(n int) []byte { return make([]byte, n) }
|
|
func defaultCheck(Option) bool { return true }
|
|
|
|
// Control represents operation that scanner should perform.
|
|
type Control byte
|
|
|
|
const (
|
|
// ControlContinue causes scanner to continue scan tokens.
|
|
ControlContinue Control = iota
|
|
// ControlBreak causes scanner to stop scan tokens.
|
|
ControlBreak
|
|
// ControlSkip causes scanner to skip current entity.
|
|
ControlSkip
|
|
)
|
|
|
|
// ScanOptions parses data in this form:
|
|
//
|
|
// values = 1#value
|
|
// value = token *( ";" param )
|
|
// param = token [ "=" (token | quoted-string) ]
|
|
//
|
|
// It calls given callback with the index of the option, option itself and its
|
|
// parameter (attribute and its value, both could be nil). Index is useful when
|
|
// header contains multiple choises for the same named option.
|
|
//
|
|
// Given callback should return one of the defined Control* values.
|
|
// ControlSkip means that passed key is not in caller's interest. That is, all
|
|
// parameters of that key will be skipped.
|
|
// ControlBreak means that no more keys and parameters should be parsed. That
|
|
// is, it must break parsing immediately.
|
|
// ControlContinue means that caller want to receive next parameter and its
|
|
// value or the next key.
|
|
//
|
|
// It returns false if data is malformed.
|
|
func ScanOptions(data []byte, it func(index int, option, attribute, value []byte) Control) bool {
|
|
lexer := &Scanner{data: data}
|
|
|
|
var ok bool
|
|
var state int
|
|
const (
|
|
stateKey = iota
|
|
stateParamBeforeName
|
|
stateParamName
|
|
stateParamBeforeValue
|
|
stateParamValue
|
|
)
|
|
|
|
var (
|
|
index int
|
|
key, param, value []byte
|
|
mustCall bool
|
|
)
|
|
for lexer.Next() {
|
|
var (
|
|
call bool
|
|
growIndex int
|
|
)
|
|
|
|
t := lexer.Type()
|
|
v := lexer.Bytes()
|
|
|
|
switch t {
|
|
case ItemToken:
|
|
switch state {
|
|
case stateKey, stateParamBeforeName:
|
|
key = v
|
|
state = stateParamBeforeName
|
|
mustCall = true
|
|
case stateParamName:
|
|
param = v
|
|
state = stateParamBeforeValue
|
|
mustCall = true
|
|
case stateParamValue:
|
|
value = v
|
|
state = stateParamBeforeName
|
|
call = true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case ItemString:
|
|
if state != stateParamValue {
|
|
return false
|
|
}
|
|
value = v
|
|
state = stateParamBeforeName
|
|
call = true
|
|
|
|
case ItemSeparator:
|
|
switch {
|
|
case isComma(v) && state == stateKey:
|
|
// Nothing to do.
|
|
|
|
case isComma(v) && state == stateParamBeforeName:
|
|
state = stateKey
|
|
// Make call only if we have not called this key yet.
|
|
call = mustCall
|
|
if !call {
|
|
// If we have already called callback with the key
|
|
// that just ended.
|
|
index++
|
|
} else {
|
|
// Else grow the index after calling callback.
|
|
growIndex = 1
|
|
}
|
|
|
|
case isComma(v) && state == stateParamBeforeValue:
|
|
state = stateKey
|
|
growIndex = 1
|
|
call = true
|
|
|
|
case isSemicolon(v) && state == stateParamBeforeName:
|
|
state = stateParamName
|
|
|
|
case isSemicolon(v) && state == stateParamBeforeValue:
|
|
state = stateParamName
|
|
call = true
|
|
|
|
case isEquality(v) && state == stateParamBeforeValue:
|
|
state = stateParamValue
|
|
|
|
default:
|
|
return false
|
|
}
|
|
|
|
default:
|
|
return false
|
|
}
|
|
|
|
if call {
|
|
switch it(index, key, param, value) {
|
|
case ControlBreak:
|
|
// User want to stop to parsing parameters.
|
|
return true
|
|
|
|
case ControlSkip:
|
|
// User want to skip current param.
|
|
state = stateKey
|
|
lexer.SkipEscaped(',')
|
|
|
|
case ControlContinue:
|
|
// User is interested in rest of parameters.
|
|
// Nothing to do.
|
|
|
|
default:
|
|
panic("unexpected control value")
|
|
}
|
|
ok = true
|
|
param = nil
|
|
value = nil
|
|
mustCall = false
|
|
index += growIndex
|
|
}
|
|
}
|
|
if mustCall {
|
|
ok = true
|
|
it(index, key, param, value)
|
|
}
|
|
|
|
return ok && !lexer.err
|
|
}
|
|
|
|
func isComma(b []byte) bool {
|
|
return len(b) == 1 && b[0] == ','
|
|
}
|
|
func isSemicolon(b []byte) bool {
|
|
return len(b) == 1 && b[0] == ';'
|
|
}
|
|
func isEquality(b []byte) bool {
|
|
return len(b) == 1 && b[0] == '='
|
|
}
|