mirror of
https://github.com/aler9/gortsplib
synced 2025-10-07 08:01:14 +08:00
use HeaderValue instead of []string; edit ReadHeaderAuth(), ReadHeaderSession(), ReadHeaderTransport() to accept HeaderValue
This commit is contained in:
60
auth.go
60
auth.go
@@ -55,8 +55,8 @@ func NewAuthServer(user string, pass string, methods []AuthMethod) *AuthServer {
|
||||
}
|
||||
|
||||
// GenerateHeader generates the WWW-Authenticate header needed by a client to log in.
|
||||
func (as *AuthServer) GenerateHeader() []string {
|
||||
var ret []string
|
||||
func (as *AuthServer) GenerateHeader() HeaderValue {
|
||||
var ret HeaderValue
|
||||
for _, m := range as.methods {
|
||||
switch m {
|
||||
case Basic:
|
||||
@@ -65,7 +65,7 @@ func (as *AuthServer) GenerateHeader() []string {
|
||||
Values: map[string]string{
|
||||
"realm": as.realm,
|
||||
},
|
||||
}).Write())
|
||||
}).Write()...)
|
||||
|
||||
case Digest:
|
||||
ret = append(ret, (&HeaderAuth{
|
||||
@@ -74,7 +74,7 @@ func (as *AuthServer) GenerateHeader() []string {
|
||||
"realm": as.realm,
|
||||
"nonce": as.nonce,
|
||||
},
|
||||
}).Write())
|
||||
}).Write()...)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
@@ -82,18 +82,18 @@ func (as *AuthServer) GenerateHeader() []string {
|
||||
|
||||
// ValidateHeader validates the Authorization header sent by a client after receiving the
|
||||
// WWW-Authenticate header provided by GenerateHeader().
|
||||
func (as *AuthServer) ValidateHeader(header []string, method Method, ur *url.URL) error {
|
||||
if len(header) == 0 {
|
||||
func (as *AuthServer) ValidateHeader(v HeaderValue, method Method, ur *url.URL) error {
|
||||
if len(v) == 0 {
|
||||
return fmt.Errorf("authorization header not provided")
|
||||
}
|
||||
if len(header) > 1 {
|
||||
if len(v) > 1 {
|
||||
return fmt.Errorf("authorization header provided multiple times")
|
||||
}
|
||||
|
||||
head := header[0]
|
||||
v0 := v[0]
|
||||
|
||||
if strings.HasPrefix(head, "Basic ") {
|
||||
inResponse := head[len("Basic "):]
|
||||
if strings.HasPrefix(v0, "Basic ") {
|
||||
inResponse := v0[len("Basic "):]
|
||||
|
||||
response := base64.StdEncoding.EncodeToString([]byte(as.user + ":" + as.pass))
|
||||
|
||||
@@ -101,8 +101,8 @@ func (as *AuthServer) ValidateHeader(header []string, method Method, ur *url.URL
|
||||
return fmt.Errorf("wrong response")
|
||||
}
|
||||
|
||||
} else if strings.HasPrefix(head, "Digest ") {
|
||||
auth, err := ReadHeaderAuth(head)
|
||||
} else if strings.HasPrefix(v0, "Digest ") {
|
||||
auth, err := ReadHeaderAuth(HeaderValue{v0})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -191,18 +191,17 @@ type authClient struct {
|
||||
|
||||
// newAuthClient allocates an authClient.
|
||||
// header is the WWW-Authenticate header provided by the server.
|
||||
func newAuthClient(header []string, user string, pass string) (*authClient, error) {
|
||||
func newAuthClient(v HeaderValue, user string, pass string) (*authClient, error) {
|
||||
// prefer digest
|
||||
headerAuthDigest := func() string {
|
||||
for _, v := range header {
|
||||
if strings.HasPrefix(v, "Digest ") {
|
||||
return v
|
||||
if headerAuthDigest := func() string {
|
||||
for _, vi := range v {
|
||||
if strings.HasPrefix(vi, "Digest ") {
|
||||
return vi
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}()
|
||||
if headerAuthDigest != "" {
|
||||
auth, err := ReadHeaderAuth(headerAuthDigest)
|
||||
}(); headerAuthDigest != "" {
|
||||
auth, err := ReadHeaderAuth(HeaderValue{headerAuthDigest})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -226,16 +225,15 @@ func newAuthClient(header []string, user string, pass string) (*authClient, erro
|
||||
}, nil
|
||||
}
|
||||
|
||||
headerAuthBasic := func() string {
|
||||
for _, v := range header {
|
||||
if strings.HasPrefix(v, "Basic ") {
|
||||
return v
|
||||
if headerAuthBasic := func() string {
|
||||
for _, vi := range v {
|
||||
if strings.HasPrefix(vi, "Basic ") {
|
||||
return vi
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}()
|
||||
if headerAuthBasic != "" {
|
||||
auth, err := ReadHeaderAuth(headerAuthBasic)
|
||||
}(); headerAuthBasic != "" {
|
||||
auth, err := ReadHeaderAuth(HeaderValue{headerAuthBasic})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -258,18 +256,18 @@ func newAuthClient(header []string, user string, pass string) (*authClient, erro
|
||||
|
||||
// GenerateHeader generates an Authorization Header that allows to authenticate a request with
|
||||
// the given method and url.
|
||||
func (ac *authClient) GenerateHeader(method Method, ur *url.URL) []string {
|
||||
func (ac *authClient) GenerateHeader(method Method, ur *url.URL) HeaderValue {
|
||||
switch ac.method {
|
||||
case Basic:
|
||||
response := base64.StdEncoding.EncodeToString([]byte(ac.user + ":" + ac.pass))
|
||||
|
||||
return []string{"Basic " + response}
|
||||
return HeaderValue{"Basic " + response}
|
||||
|
||||
case Digest:
|
||||
response := md5Hex(md5Hex(ac.user+":"+ac.realm+":"+ac.pass) + ":" +
|
||||
ac.nonce + ":" + md5Hex(string(method)+":"+ur.String()))
|
||||
|
||||
return []string{(&HeaderAuth{
|
||||
return (&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
"username": ac.user,
|
||||
@@ -278,7 +276,7 @@ func (ac *authClient) GenerateHeader(method Method, ur *url.URL) []string {
|
||||
"uri": ur.String(),
|
||||
"response": response,
|
||||
},
|
||||
}).Write()}
|
||||
}).Write()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -112,7 +112,7 @@ func (c *ConnClient) Do(req *Request) (*Response, error) {
|
||||
|
||||
// insert session
|
||||
if c.session != "" {
|
||||
req.Header["Session"] = []string{c.session}
|
||||
req.Header["Session"] = HeaderValue{c.session}
|
||||
}
|
||||
|
||||
// insert auth
|
||||
@@ -129,7 +129,7 @@ func (c *ConnClient) Do(req *Request) (*Response, error) {
|
||||
|
||||
// insert cseq
|
||||
c.curCSeq += 1
|
||||
req.Header["CSeq"] = []string{strconv.FormatInt(int64(c.curCSeq), 10)}
|
||||
req.Header["CSeq"] = HeaderValue{strconv.FormatInt(int64(c.curCSeq), 10)}
|
||||
|
||||
c.conf.Conn.SetWriteDeadline(time.Now().Add(c.conf.WriteTimeout))
|
||||
err := req.write(c.bw)
|
||||
@@ -148,8 +148,8 @@ func (c *ConnClient) Do(req *Request) (*Response, error) {
|
||||
}
|
||||
|
||||
// get session from response
|
||||
if sxRaw, ok := res.Header["Session"]; ok && len(sxRaw) == 1 {
|
||||
sx, err := ReadHeaderSession(sxRaw[0])
|
||||
if v, ok := res.Header["Session"]; ok {
|
||||
sx, err := ReadHeaderSession(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse session header: %s", err)
|
||||
}
|
||||
@@ -301,7 +301,7 @@ func (c *ConnClient) setup(u *url.URL, media *sdp.MediaDescription, transport []
|
||||
Method: SETUP,
|
||||
Url: u,
|
||||
Header: Header{
|
||||
"Transport": []string{strings.Join(transport, ";")},
|
||||
"Transport": HeaderValue{strings.Join(transport, ";")},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -329,12 +329,11 @@ func (c *ConnClient) SetupUdp(u *url.URL, track *Track, rtpPort int,
|
||||
return 0, 0, nil, err
|
||||
}
|
||||
|
||||
tsRaw, ok := res.Header["Transport"]
|
||||
if !ok || len(tsRaw) != 1 {
|
||||
return 0, 0, nil, fmt.Errorf("SETUP: transport header not provided")
|
||||
th, err := ReadHeaderTransport(res.Header["Transport"])
|
||||
if err != nil {
|
||||
return 0, 0, nil, fmt.Errorf("SETUP: transport header: %s", err)
|
||||
}
|
||||
|
||||
th := ReadHeaderTransport(tsRaw[0])
|
||||
rtpServerPort, rtcpServerPort := th.GetPorts("server_port")
|
||||
if rtpServerPort == 0 {
|
||||
return 0, 0, nil, fmt.Errorf("SETUP: server ports not provided")
|
||||
@@ -357,15 +356,14 @@ func (c *ConnClient) SetupTcp(u *url.URL, track *Track) (*Response, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tsRaw, ok := res.Header["Transport"]
|
||||
if !ok || len(tsRaw) != 1 {
|
||||
return nil, fmt.Errorf("SETUP: transport header not provided")
|
||||
th, err := ReadHeaderTransport(res.Header["Transport"])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SETUP: transport header: %s", err)
|
||||
}
|
||||
th := ReadHeaderTransport(tsRaw[0])
|
||||
|
||||
_, ok = th[interleaved]
|
||||
_, ok := th[interleaved]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("SETUP: transport header does not have %s (%s)", interleaved, tsRaw[0])
|
||||
return nil, fmt.Errorf("SETUP: transport header does not have %s (%s)", interleaved, res.Header["Transport"])
|
||||
}
|
||||
|
||||
return res, nil
|
||||
|
@@ -16,23 +16,33 @@ type HeaderAuth struct {
|
||||
var regHeaderAuthKeyValue = regexp.MustCompile("^([a-z]+)=(\"(.*?)\"|([a-zA-Z0-9]+))(, *|$)")
|
||||
|
||||
// ReadHeaderAuth parses an Authenticate or a WWW-Authenticate header.
|
||||
func ReadHeaderAuth(in string) (*HeaderAuth, error) {
|
||||
func ReadHeaderAuth(v HeaderValue) (*HeaderAuth, error) {
|
||||
if len(v) == 0 {
|
||||
return nil, fmt.Errorf("value not provided")
|
||||
}
|
||||
|
||||
if len(v) > 1 {
|
||||
return nil, fmt.Errorf("value provided multiple times (%v)", v)
|
||||
}
|
||||
|
||||
ha := &HeaderAuth{
|
||||
Values: make(map[string]string),
|
||||
}
|
||||
|
||||
i := strings.IndexByte(in, ' ')
|
||||
if i < 0 {
|
||||
return nil, fmt.Errorf("unable to find prefix (%s)", in)
|
||||
}
|
||||
ha.Prefix, in = in[:i], in[i+1:]
|
||||
v0 := v[0]
|
||||
|
||||
for len(in) > 0 {
|
||||
m := regHeaderAuthKeyValue.FindStringSubmatch(in)
|
||||
i := strings.IndexByte(v[0], ' ')
|
||||
if i < 0 {
|
||||
return nil, fmt.Errorf("unable to find prefix (%s)", v0)
|
||||
}
|
||||
ha.Prefix, v0 = v0[:i], v0[i+1:]
|
||||
|
||||
for len(v0) > 0 {
|
||||
m := regHeaderAuthKeyValue.FindStringSubmatch(v0)
|
||||
if m == nil {
|
||||
return nil, fmt.Errorf("unable to parse key-value (%s)", in)
|
||||
return nil, fmt.Errorf("unable to parse key-value (%s)", v0)
|
||||
}
|
||||
in = in[len(m[0]):]
|
||||
v0 = v0[len(m[0]):]
|
||||
|
||||
m[2] = strings.TrimPrefix(m[2], "\"")
|
||||
m[2] = strings.TrimSuffix(m[2], "\"")
|
||||
@@ -43,7 +53,7 @@ func ReadHeaderAuth(in string) (*HeaderAuth, error) {
|
||||
}
|
||||
|
||||
// Write encodes an Authenticate or a WWW-Authenticate header.
|
||||
func (ha *HeaderAuth) Write() string {
|
||||
func (ha *HeaderAuth) Write() HeaderValue {
|
||||
ret := ha.Prefix + " "
|
||||
|
||||
// always put realm first, otherwise VLC does not send back the response
|
||||
@@ -64,5 +74,5 @@ func (ha *HeaderAuth) Write() string {
|
||||
}
|
||||
ret += strings.Join(tmp, ", ")
|
||||
|
||||
return ret
|
||||
return HeaderValue{ret}
|
||||
}
|
||||
|
@@ -8,14 +8,14 @@ import (
|
||||
|
||||
var casesHeaderAuth = []struct {
|
||||
name string
|
||||
dec string
|
||||
enc string
|
||||
dec HeaderValue
|
||||
enc HeaderValue
|
||||
ha *HeaderAuth
|
||||
}{
|
||||
{
|
||||
"basic",
|
||||
`Basic realm="4419b63f5e51"`,
|
||||
`Basic realm="4419b63f5e51"`,
|
||||
HeaderValue{`Basic realm="4419b63f5e51"`},
|
||||
HeaderValue{`Basic realm="4419b63f5e51"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Basic",
|
||||
Values: map[string]string{
|
||||
@@ -25,8 +25,8 @@ var casesHeaderAuth = []struct {
|
||||
},
|
||||
{
|
||||
"digest request 1",
|
||||
`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`,
|
||||
`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`,
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
@@ -38,8 +38,8 @@ var casesHeaderAuth = []struct {
|
||||
},
|
||||
{
|
||||
"digest request 2",
|
||||
`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale=FALSE`,
|
||||
`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`,
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale=FALSE`},
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
@@ -51,8 +51,8 @@ var casesHeaderAuth = []struct {
|
||||
},
|
||||
{
|
||||
"digest request 3",
|
||||
`Digest realm="4419b63f5e51",nonce="133767111917411116111311118211673010032", stale="FALSE"`,
|
||||
`Digest realm="4419b63f5e51", nonce="133767111917411116111311118211673010032", stale="FALSE"`,
|
||||
HeaderValue{`Digest realm="4419b63f5e51",nonce="133767111917411116111311118211673010032", stale="FALSE"`},
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="133767111917411116111311118211673010032", stale="FALSE"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
@@ -64,8 +64,8 @@ var casesHeaderAuth = []struct {
|
||||
},
|
||||
{
|
||||
"digest response generic",
|
||||
`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`,
|
||||
`Digest realm="bb", nonce="cc", response="ee", uri="dd", username="aa"`,
|
||||
HeaderValue{`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`},
|
||||
HeaderValue{`Digest realm="bb", nonce="cc", response="ee", uri="dd", username="aa"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
@@ -79,8 +79,8 @@ var casesHeaderAuth = []struct {
|
||||
},
|
||||
{
|
||||
"digest response with empty field",
|
||||
`Digest username="", realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", response="c072ae90eb4a27f4cdcb90d62266b2a1"`,
|
||||
`Digest realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", response="c072ae90eb4a27f4cdcb90d62266b2a1", uri="rtsp://localhost:8554/mystream", username=""`,
|
||||
HeaderValue{`Digest username="", realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", response="c072ae90eb4a27f4cdcb90d62266b2a1"`},
|
||||
HeaderValue{`Digest realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", response="c072ae90eb4a27f4cdcb90d62266b2a1", uri="rtsp://localhost:8554/mystream", username=""`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
@@ -94,8 +94,8 @@ var casesHeaderAuth = []struct {
|
||||
},
|
||||
{
|
||||
"digest response with no spaces and additional fields",
|
||||
`Digest realm="Please log in with a valid username",nonce="752a62306daf32b401a41004555c7663",opaque="",stale=FALSE,algorithm=MD5`,
|
||||
`Digest realm="Please log in with a valid username", algorithm="MD5", nonce="752a62306daf32b401a41004555c7663", opaque="", stale="FALSE"`,
|
||||
HeaderValue{`Digest realm="Please log in with a valid username",nonce="752a62306daf32b401a41004555c7663",opaque="",stale=FALSE,algorithm=MD5`},
|
||||
HeaderValue{`Digest realm="Please log in with a valid username", algorithm="MD5", nonce="752a62306daf32b401a41004555c7663", opaque="", stale="FALSE"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
|
@@ -13,8 +13,16 @@ type HeaderSession struct {
|
||||
}
|
||||
|
||||
// ReadHeaderSession parses a Session header.
|
||||
func ReadHeaderSession(in string) (*HeaderSession, error) {
|
||||
parts := strings.Split(in, ";")
|
||||
func ReadHeaderSession(v HeaderValue) (*HeaderSession, error) {
|
||||
if len(v) == 0 {
|
||||
return nil, fmt.Errorf("value not provided")
|
||||
}
|
||||
|
||||
if len(v) > 1 {
|
||||
return nil, fmt.Errorf("value provided multiple times (%v)", v)
|
||||
}
|
||||
|
||||
parts := strings.Split(v[0], ";")
|
||||
if len(parts) == 0 {
|
||||
return nil, fmt.Errorf("invalid value")
|
||||
}
|
||||
|
@@ -7,20 +7,20 @@ import (
|
||||
)
|
||||
|
||||
var casesHeaderSession = []struct {
|
||||
name string
|
||||
byts string
|
||||
hs *HeaderSession
|
||||
name string
|
||||
value HeaderValue
|
||||
hs *HeaderSession
|
||||
}{
|
||||
{
|
||||
"base",
|
||||
`A3eqwsafq3rFASqew`,
|
||||
HeaderValue{`A3eqwsafq3rFASqew`},
|
||||
&HeaderSession{
|
||||
Session: "A3eqwsafq3rFASqew",
|
||||
},
|
||||
},
|
||||
{
|
||||
"with timeout",
|
||||
`A3eqwsafq3rFASqew;timeout=47`,
|
||||
HeaderValue{`A3eqwsafq3rFASqew;timeout=47`},
|
||||
&HeaderSession{
|
||||
Session: "A3eqwsafq3rFASqew",
|
||||
Timeout: func() *uint {
|
||||
@@ -31,7 +31,7 @@ var casesHeaderSession = []struct {
|
||||
},
|
||||
{
|
||||
"with timeout and space",
|
||||
`A3eqwsafq3rFASqew; timeout=47`,
|
||||
HeaderValue{`A3eqwsafq3rFASqew; timeout=47`},
|
||||
&HeaderSession{
|
||||
Session: "A3eqwsafq3rFASqew",
|
||||
Timeout: func() *uint {
|
||||
@@ -45,7 +45,7 @@ var casesHeaderSession = []struct {
|
||||
func TestHeaderSession(t *testing.T) {
|
||||
for _, c := range casesHeaderSession {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
req, err := ReadHeaderSession(c.byts)
|
||||
req, err := ReadHeaderSession(c.value)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.hs, req)
|
||||
})
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package gortsplib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -9,12 +10,21 @@ import (
|
||||
type HeaderTransport map[string]struct{}
|
||||
|
||||
// ReadHeaderTransport parses a Transport header.
|
||||
func ReadHeaderTransport(in string) HeaderTransport {
|
||||
func ReadHeaderTransport(v HeaderValue) (HeaderTransport, error) {
|
||||
if len(v) == 0 {
|
||||
return nil, fmt.Errorf("value not provided")
|
||||
}
|
||||
|
||||
if len(v) > 1 {
|
||||
return nil, fmt.Errorf("value provided multiple times (%v)", v)
|
||||
}
|
||||
|
||||
ht := make(map[string]struct{})
|
||||
for _, t := range strings.Split(in, ";") {
|
||||
for _, t := range strings.Split(v[0], ";") {
|
||||
ht[t] = struct{}{}
|
||||
}
|
||||
return ht
|
||||
|
||||
return ht, nil
|
||||
}
|
||||
|
||||
// GetValue gets a value from the header.
|
||||
|
@@ -28,8 +28,11 @@ func headerKeyNormalize(in string) string {
|
||||
return http.CanonicalHeaderKey(in)
|
||||
}
|
||||
|
||||
// HeaderValue is an header value.
|
||||
type HeaderValue []string
|
||||
|
||||
// Header is a RTSP reader, present in both Requests and Responses.
|
||||
type Header map[string][]string
|
||||
type Header map[string]HeaderValue
|
||||
|
||||
func headerRead(rb *bufio.Reader) (Header, error) {
|
||||
h := make(Header)
|
||||
|
@@ -23,8 +23,8 @@ var casesHeader = []struct {
|
||||
"Require: implicit-play\r\n" +
|
||||
"\r\n"),
|
||||
Header{
|
||||
"Require": []string{"implicit-play"},
|
||||
"Proxy-Require": []string{"gzipped-messages"},
|
||||
"Require": HeaderValue{"implicit-play"},
|
||||
"Proxy-Require": HeaderValue{"gzipped-messages"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -36,7 +36,7 @@ var casesHeader = []struct {
|
||||
"WWW-Authenticate: Basic realm=\"4419b63f5e51\"\r\n" +
|
||||
"\r\n"),
|
||||
Header{
|
||||
"WWW-Authenticate": []string{
|
||||
"WWW-Authenticate": HeaderValue{
|
||||
`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`,
|
||||
`Basic realm="4419b63f5e51"`,
|
||||
},
|
||||
@@ -49,7 +49,7 @@ var casesHeader = []struct {
|
||||
[]byte("CSeq: 2\r\n" +
|
||||
"\r\n"),
|
||||
Header{
|
||||
"CSeq": []string{"2"},
|
||||
"CSeq": HeaderValue{"2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -59,7 +59,7 @@ var casesHeader = []struct {
|
||||
[]byte("CSeq: 2\r\n" +
|
||||
"\r\n"),
|
||||
Header{
|
||||
"CSeq": []string{"2"},
|
||||
"CSeq": HeaderValue{"2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -71,8 +71,8 @@ var casesHeader = []struct {
|
||||
"Content-Type: testing\r\n" +
|
||||
"\r\n"),
|
||||
Header{
|
||||
"Content-Length": []string{"value"},
|
||||
"Content-Type": []string{"testing"},
|
||||
"Content-Length": HeaderValue{"value"},
|
||||
"Content-Type": HeaderValue{"testing"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -84,8 +84,8 @@ var casesHeader = []struct {
|
||||
"WWW-Authenticate: value\r\n" +
|
||||
"\r\n"),
|
||||
Header{
|
||||
"CSeq": []string{"value"},
|
||||
"WWW-Authenticate": []string{"value"},
|
||||
"CSeq": HeaderValue{"value"},
|
||||
"WWW-Authenticate": HeaderValue{"value"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@@ -25,9 +25,9 @@ var casesRequest = []struct {
|
||||
Method: "OPTIONS",
|
||||
Url: &url.URL{Scheme: "rtsp", Host: "example.com", Path: "/media.mp4"},
|
||||
Header: Header{
|
||||
"CSeq": []string{"1"},
|
||||
"Require": []string{"implicit-play"},
|
||||
"Proxy-Require": []string{"gzipped-messages"},
|
||||
"CSeq": HeaderValue{"1"},
|
||||
"Require": HeaderValue{"implicit-play"},
|
||||
"Proxy-Require": HeaderValue{"gzipped-messages"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -40,7 +40,7 @@ var casesRequest = []struct {
|
||||
Method: "DESCRIBE",
|
||||
Url: &url.URL{Scheme: "rtsp", Host: "example.com", Path: "/media.mp4"},
|
||||
Header: Header{
|
||||
"CSeq": []string{"2"},
|
||||
"CSeq": HeaderValue{"2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -68,11 +68,11 @@ var casesRequest = []struct {
|
||||
Method: "ANNOUNCE",
|
||||
Url: &url.URL{Scheme: "rtsp", Host: "example.com", Path: "/media.mp4"},
|
||||
Header: Header{
|
||||
"CSeq": []string{"7"},
|
||||
"Date": []string{"23 Jan 1997 15:35:06 GMT"},
|
||||
"Session": []string{"12345678"},
|
||||
"Content-Type": []string{"application/sdp"},
|
||||
"Content-Length": []string{"306"},
|
||||
"CSeq": HeaderValue{"7"},
|
||||
"Date": HeaderValue{"23 Jan 1997 15:35:06 GMT"},
|
||||
"Session": HeaderValue{"12345678"},
|
||||
"Content-Type": HeaderValue{"application/sdp"},
|
||||
"Content-Length": HeaderValue{"306"},
|
||||
},
|
||||
Content: []byte("v=0\n" +
|
||||
"o=mhandley 2890844526 2890845468 IN IP4 126.16.64.4\n" +
|
||||
@@ -102,10 +102,10 @@ var casesRequest = []struct {
|
||||
Method: "GET_PARAMETER",
|
||||
Url: &url.URL{Scheme: "rtsp", Host: "example.com", Path: "/media.mp4"},
|
||||
Header: Header{
|
||||
"CSeq": []string{"9"},
|
||||
"Content-Type": []string{"text/parameters"},
|
||||
"Session": []string{"12345678"},
|
||||
"Content-Length": []string{"24"},
|
||||
"CSeq": HeaderValue{"9"},
|
||||
"Content-Type": HeaderValue{"text/parameters"},
|
||||
"Session": HeaderValue{"12345678"},
|
||||
"Content-Length": HeaderValue{"24"},
|
||||
},
|
||||
Content: []byte("packets_received\n" +
|
||||
"jitter\n",
|
||||
|
@@ -198,7 +198,7 @@ func (res *Response) write(bw *bufio.Writer) error {
|
||||
}
|
||||
|
||||
if len(res.Content) != 0 {
|
||||
res.Header["Content-Length"] = []string{strconv.FormatInt(int64(len(res.Content)), 10)}
|
||||
res.Header["Content-Length"] = HeaderValue{strconv.FormatInt(int64(len(res.Content)), 10)}
|
||||
}
|
||||
|
||||
err = res.Header.write(bw)
|
||||
|
@@ -24,8 +24,8 @@ var casesResponse = []struct {
|
||||
StatusCode: StatusOK,
|
||||
StatusMessage: "OK",
|
||||
Header: Header{
|
||||
"CSeq": []string{"1"},
|
||||
"Public": []string{"DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE"},
|
||||
"CSeq": HeaderValue{"1"},
|
||||
"Public": HeaderValue{"DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -43,13 +43,13 @@ var casesResponse = []struct {
|
||||
StatusCode: StatusOK,
|
||||
StatusMessage: "OK",
|
||||
Header: Header{
|
||||
"CSeq": []string{"2"},
|
||||
"Session": []string{"645252166"},
|
||||
"WWW-Authenticate": []string{
|
||||
"CSeq": HeaderValue{"2"},
|
||||
"Session": HeaderValue{"645252166"},
|
||||
"WWW-Authenticate": HeaderValue{
|
||||
"Digest realm=\"4419b63f5e51\", nonce=\"8b84a3b789283a8bea8da7fa7d41f08b\", stale=\"FALSE\"",
|
||||
"Basic realm=\"4419b63f5e51\"",
|
||||
},
|
||||
"Date": []string{"Sat, Aug 16 2014 02:22:28 GMT"},
|
||||
"Date": HeaderValue{"Sat, Aug 16 2014 02:22:28 GMT"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -82,10 +82,10 @@ var casesResponse = []struct {
|
||||
StatusCode: 200,
|
||||
StatusMessage: "OK",
|
||||
Header: Header{
|
||||
"Content-Base": []string{"rtsp://example.com/media.mp4"},
|
||||
"Content-Length": []string{"444"},
|
||||
"Content-Type": []string{"application/sdp"},
|
||||
"CSeq": []string{"2"},
|
||||
"Content-Base": HeaderValue{"rtsp://example.com/media.mp4"},
|
||||
"Content-Length": HeaderValue{"444"},
|
||||
"Content-Type": HeaderValue{"application/sdp"},
|
||||
"CSeq": HeaderValue{"2"},
|
||||
},
|
||||
Content: []byte("m=video 0 RTP/AVP 96\n" +
|
||||
"a=control:streamid=0\n" +
|
||||
@@ -135,13 +135,13 @@ func TestResponseWriteStatusAutofill(t *testing.T) {
|
||||
res := &Response{
|
||||
StatusCode: StatusMethodNotAllowed,
|
||||
Header: Header{
|
||||
"CSeq": []string{"2"},
|
||||
"Session": []string{"645252166"},
|
||||
"WWW-Authenticate": []string{
|
||||
"CSeq": HeaderValue{"2"},
|
||||
"Session": HeaderValue{"645252166"},
|
||||
"WWW-Authenticate": HeaderValue{
|
||||
"Digest realm=\"4419b63f5e51\", nonce=\"8b84a3b789283a8bea8da7fa7d41f08b\", stale=\"FALSE\"",
|
||||
"Basic realm=\"4419b63f5e51\"",
|
||||
},
|
||||
"Date": []string{"Sat, Aug 16 2014 02:22:28 GMT"},
|
||||
"Date": HeaderValue{"Sat, Aug 16 2014 02:22:28 GMT"},
|
||||
},
|
||||
}
|
||||
byts := []byte("RTSP/1.0 405 Method Not Allowed\r\n" +
|
||||
|
Reference in New Issue
Block a user