diff --git a/client.go b/client.go index 3a32f961..550a773d 100644 --- a/client.go +++ b/client.go @@ -1211,15 +1211,19 @@ func (c *Client) doDescribe(u *base.URL) (Tracks, *base.URL, *base.Response, err len(res.Header["Location"]) == 1 { c.reset() - u, err := base.ParseURL(res.Header["Location"][0]) + ru, err := base.ParseURL(res.Header["Location"][0]) if err != nil { return nil, nil, nil, err } - c.scheme = u.Scheme - c.host = u.Host + if u.User != nil { + ru.User = u.User + } - return c.doDescribe(u) + c.scheme = ru.Scheme + c.host = ru.Host + + return c.doDescribe(ru) } return nil, nil, res, liberrors.ErrClientBadStatusCode{Code: res.StatusCode, Message: res.StatusMessage} diff --git a/client_read_test.go b/client_read_test.go index 408b1ed4..0d0b543b 100644 --- a/client_read_test.go +++ b/client_read_test.go @@ -1573,154 +1573,200 @@ func TestClientReadDifferentInterleavedIDs(t *testing.T) { } func TestClientReadRedirect(t *testing.T) { - l, err := net.Listen("tcp", "localhost:8554") - require.NoError(t, err) - defer l.Close() + for _, withCredentials := range []bool{false, true} { + runName := "WithoutCredentials" + if withCredentials { + runName = "WithCredentials" + } + t.Run(runName, func(t *testing.T) { + packetRecv := make(chan struct{}) - serverDone := make(chan struct{}) - defer func() { <-serverDone }() - go func() { - defer close(serverDone) + c := Client{ + OnPacketRTP: func(ctx *ClientOnPacketRTPCtx) { + close(packetRecv) + }, + } - conn, err := l.Accept() - require.NoError(t, err) - br := bufio.NewReader(conn) + l, err := net.Listen("tcp", "localhost:8554") + require.NoError(t, err) + defer l.Close() - req, err := readRequest(br) - require.NoError(t, err) - require.Equal(t, base.Options, req.Method) + serverDone := make(chan struct{}) + defer func() { <-serverDone }() + go func() { + defer close(serverDone) - byts, _ := base.Response{ - StatusCode: base.StatusOK, - Header: base.Header{ - "Public": base.HeaderValue{strings.Join([]string{ - string(base.Describe), - string(base.Setup), - string(base.Play), - }, ", ")}, - }, - }.Write() - _, err = conn.Write(byts) - require.NoError(t, err) + conn, err := l.Accept() + require.NoError(t, err) + br := bufio.NewReader(conn) - req, err = readRequest(br) - require.NoError(t, err) - require.Equal(t, base.Describe, req.Method) + req, err := readRequest(br) + require.NoError(t, err) + require.Equal(t, base.Options, req.Method) - byts, _ = base.Response{ - StatusCode: base.StatusMovedPermanently, - Header: base.Header{ - "Location": base.HeaderValue{"rtsp://localhost:8554/test"}, - }, - }.Write() - _, err = conn.Write(byts) - require.NoError(t, err) + byts, _ := base.Response{ + StatusCode: base.StatusOK, + Header: base.Header{ + "Public": base.HeaderValue{strings.Join([]string{ + string(base.Describe), + string(base.Setup), + string(base.Play), + }, ", ")}, + }, + }.Write() + _, err = conn.Write(byts) + require.NoError(t, err) - conn.Close() + req, err = readRequest(br) + require.NoError(t, err) + require.Equal(t, base.Describe, req.Method) - conn, err = l.Accept() - require.NoError(t, err) - defer conn.Close() - br = bufio.NewReader(conn) + byts, _ = base.Response{ + StatusCode: base.StatusMovedPermanently, + Header: base.Header{ + "Location": base.HeaderValue{"rtsp://localhost:8554/test"}, + }, + }.Write() + _, err = conn.Write(byts) + require.NoError(t, err) - req, err = readRequest(br) - require.NoError(t, err) - require.Equal(t, base.Options, req.Method) + conn.Close() - byts, _ = base.Response{ - StatusCode: base.StatusOK, - Header: base.Header{ - "Public": base.HeaderValue{strings.Join([]string{ - string(base.Describe), - string(base.Setup), - string(base.Play), - }, ", ")}, - }, - }.Write() - _, err = conn.Write(byts) - require.NoError(t, err) + conn, err = l.Accept() + require.NoError(t, err) + defer conn.Close() + br = bufio.NewReader(conn) - req, err = readRequest(br) - require.NoError(t, err) - require.Equal(t, base.Describe, req.Method) + req, err = readRequest(br) + require.NoError(t, err) + require.Equal(t, base.Options, req.Method) - track, err := NewTrackH264(96, []byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil) - require.NoError(t, err) + byts, _ = base.Response{ + StatusCode: base.StatusOK, + Header: base.Header{ + "Public": base.HeaderValue{strings.Join([]string{ + string(base.Describe), + string(base.Setup), + string(base.Play), + }, ", ")}, + }, + }.Write() - tracks := Tracks{track} - tracks.setControls() + _, err = conn.Write(byts) + require.NoError(t, err) - byts, _ = base.Response{ - StatusCode: base.StatusOK, - Header: base.Header{ - "Content-Type": base.HeaderValue{"application/sdp"}, - "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, - }, - Body: tracks.Write(false), - }.Write() - _, err = conn.Write(byts) - require.NoError(t, err) + req, err = readRequest(br) + require.NoError(t, err) + require.Equal(t, base.Describe, req.Method) - req, err = readRequest(br) - require.NoError(t, err) - require.Equal(t, base.Setup, req.Method) + if withCredentials { + if _, exists := req.Header["Authorization"]; !exists { + authRealm := "example@localhost" + authNonce := "exampleNonce" + authOpaque := "exampleOpaque" + authStale := "FALSE" + authAlg := "MD5" + byts, _ = base.Response{ + Header: base.Header{ + "WWW-Authenticate": headers.Authenticate{ + Method: headers.AuthDigest, + Realm: &authRealm, + Nonce: &authNonce, + Opaque: &authOpaque, + Stale: &authStale, + Algorithm: &authAlg, + }.Write(), + }, + StatusCode: base.StatusUnauthorized, + }.Write() + _, err = conn.Write(byts) + require.NoError(t, err) + } + req, err = readRequest(br) + require.NoError(t, err) + authHeaderVal, exists := req.Header["Authorization"] + require.True(t, exists) + var authHeader headers.Authenticate + require.NoError(t, authHeader.Read(authHeaderVal)) + require.Equal(t, *authHeader.Username, "testusr") + require.Equal(t, base.Describe, req.Method) + } - var th headers.Transport - err = th.Read(req.Header["Transport"]) - require.NoError(t, err) + track, err := NewTrackH264(96, []byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil) + require.NoError(t, err) - byts, _ = base.Response{ - StatusCode: base.StatusOK, - Header: base.Header{ - "Transport": headers.Transport{ - Protocol: headers.TransportProtocolUDP, - Delivery: func() *headers.TransportDelivery { - v := headers.TransportDeliveryUnicast - return &v - }(), - ClientPorts: th.ClientPorts, - ServerPorts: &[2]int{34556, 34557}, - }.Write(), - }, - }.Write() - _, err = conn.Write(byts) - require.NoError(t, err) + tracks := Tracks{track} + tracks.setControls() - req, err = readRequest(br) - require.NoError(t, err) - require.Equal(t, base.Play, req.Method) + byts, _ = base.Response{ + StatusCode: base.StatusOK, + Header: base.Header{ + "Content-Type": base.HeaderValue{"application/sdp"}, + "Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"}, + }, + Body: tracks.Write(false), + }.Write() + _, err = conn.Write(byts) + require.NoError(t, err) - byts, _ = base.Response{ - StatusCode: base.StatusOK, - }.Write() - _, err = conn.Write(byts) - require.NoError(t, err) + req, err = readRequest(br) + require.NoError(t, err) + require.Equal(t, base.Setup, req.Method) - time.Sleep(500 * time.Millisecond) + var th headers.Transport + err = th.Read(req.Header["Transport"]) + require.NoError(t, err) - l1, err := net.ListenPacket("udp", "localhost:34556") - require.NoError(t, err) - defer l1.Close() + byts, _ = base.Response{ + StatusCode: base.StatusOK, + Header: base.Header{ + "Transport": headers.Transport{ + Protocol: headers.TransportProtocolUDP, + Delivery: func() *headers.TransportDelivery { + v := headers.TransportDeliveryUnicast + return &v + }(), + ClientPorts: th.ClientPorts, + ServerPorts: &[2]int{34556, 34557}, + }.Write(), + }, + }.Write() + _, err = conn.Write(byts) + require.NoError(t, err) - l1.WriteTo(testRTPPacketMarshaled, &net.UDPAddr{ - IP: net.ParseIP("127.0.0.1"), - Port: th.ClientPorts[0], + req, err = readRequest(br) + require.NoError(t, err) + require.Equal(t, base.Play, req.Method) + + byts, _ = base.Response{ + StatusCode: base.StatusOK, + }.Write() + _, err = conn.Write(byts) + require.NoError(t, err) + + time.Sleep(500 * time.Millisecond) + + l1, err := net.ListenPacket("udp", "localhost:34556") + require.NoError(t, err) + defer l1.Close() + + l1.WriteTo(testRTPPacketMarshaled, &net.UDPAddr{ + IP: net.ParseIP("127.0.0.1"), + Port: th.ClientPorts[0], + }) + }() + + ru := "rtsp://localhost:8554/path1" + if withCredentials { + ru = "rtsp://testusr:testpwd@localhost:8554/path1" + } + err = c.StartReading(ru) + require.NoError(t, err) + defer c.Close() + + <-packetRecv }) - }() - - packetRecv := make(chan struct{}) - - c := Client{ - OnPacketRTP: func(ctx *ClientOnPacketRTPCtx) { - close(packetRecv) - }, } - - err = c.StartReading("rtsp://localhost:8554/path1") - require.NoError(t, err) - defer c.Close() - - <-packetRecv } func TestClientReadPause(t *testing.T) {