diff --git a/auth.go b/auth.go index 59b49850..a7cb0128 100644 --- a/auth.go +++ b/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 diff --git a/conn-client.go b/conn-client.go index c455e0c3..0649fca6 100644 --- a/conn-client.go +++ b/conn-client.go @@ -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 diff --git a/header-auth.go b/header-auth.go index e030af2a..652ae349 100644 --- a/header-auth.go +++ b/header-auth.go @@ -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} } diff --git a/header-auth_test.go b/header-auth_test.go index c7740458..41f46fd3 100644 --- a/header-auth_test.go +++ b/header-auth_test.go @@ -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{ diff --git a/header-session.go b/header-session.go index ccd3b892..a89827a9 100644 --- a/header-session.go +++ b/header-session.go @@ -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") } diff --git a/header-session_test.go b/header-session_test.go index 01d13a90..b8cfd3dd 100644 --- a/header-session_test.go +++ b/header-session_test.go @@ -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) }) diff --git a/header-transport.go b/header-transport.go index 0b611bea..cfc242fe 100644 --- a/header-transport.go +++ b/header-transport.go @@ -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. diff --git a/header.go b/header.go index 2a9a1843..945ec9a5 100644 --- a/header.go +++ b/header.go @@ -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) diff --git a/header_test.go b/header_test.go index 77a6afd2..5467c9a6 100644 --- a/header_test.go +++ b/header_test.go @@ -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"}, }, }, } diff --git a/request_test.go b/request_test.go index 95dc55b3..923be4f4 100644 --- a/request_test.go +++ b/request_test.go @@ -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", diff --git a/response.go b/response.go index 53199289..60bead40 100644 --- a/response.go +++ b/response.go @@ -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) diff --git a/response_test.go b/response_test.go index 5eafe6e7..dc184063 100644 --- a/response_test.go +++ b/response_test.go @@ -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" +