Prefers DIGEST when RTSP servers sends both BASIC and DIGEST auth info.

Issue: google/ExoPlayer#9800

Added test for RTSP authentication.

PiperOrigin-RevId: 420048821
This commit is contained in:
claincly 2022-01-06 14:34:55 +00:00 committed by bachinger
parent e2c4fd80d3
commit 152a1650f6
6 changed files with 118 additions and 15 deletions

View File

@ -554,14 +554,23 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
case 401:
if (rtspAuthUserInfo != null && !receivedAuthorizationRequest) {
// Unauthorized.
@Nullable
String wwwAuthenticateHeader = response.headers.get(RtspHeaders.WWW_AUTHENTICATE);
if (wwwAuthenticateHeader == null) {
ImmutableList<String> wwwAuthenticateHeaders =
response.headers.values(RtspHeaders.WWW_AUTHENTICATE);
if (wwwAuthenticateHeaders.isEmpty()) {
throw ParserException.createForMalformedManifest(
"Missing WWW-Authenticate header in a 401 response.", /* cause= */ null);
}
rtspAuthenticationInfo =
RtspMessageUtil.parseWwwAuthenticateHeader(wwwAuthenticateHeader);
for (int i = 0; i < wwwAuthenticateHeaders.size(); i++) {
rtspAuthenticationInfo =
RtspMessageUtil.parseWwwAuthenticateHeader(wwwAuthenticateHeaders.get(i));
if (rtspAuthenticationInfo.authenticationMechanism
== RtspAuthenticationInfo.DIGEST) {
// Prefers DIGEST when RTSP servers sends both BASIC and DIGEST auth info.
break;
}
}
messageSender.retryLastRequest();
receivedAuthorizationRequest = true;
return;

View File

@ -92,7 +92,7 @@ public final class RtspClientTest {
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
return RtspTestUtils.newDescribeResponseWithSdpMessage(
SESSION_DESCRIPTION, rtpPacketStreamDumps, requestedUri);
}
@ -167,7 +167,7 @@ public final class RtspClientTest {
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
return RtspTestUtils.newDescribeResponseWithSdpMessage(
SESSION_DESCRIPTION, rtpPacketStreamDumps, requestedUri);
}
@ -209,7 +209,7 @@ public final class RtspClientTest {
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
if (!requestedUri.getPath().contains("redirect")) {
return new RtspResponse(
301,
@ -263,7 +263,7 @@ public final class RtspClientTest {
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
return RtspTestUtils.newDescribeResponseWithSdpMessage(
SESSION_DESCRIPTION, rtpPacketStreamDumps, requestedUri);
}
@ -310,7 +310,7 @@ public final class RtspClientTest {
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
clientHasSentDescribeRequest.set(true);
return RtspTestUtils.RTSP_ERROR_METHOD_NOT_ALLOWED;
}
@ -356,7 +356,7 @@ public final class RtspClientTest {
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
// This session description misses required the o, t and s tags.
return RtspTestUtils.newDescribeResponseWithSdpMessage(
/* sessionDescription= */ "v=0\r\n", rtpPacketStreamDumps, requestedUri);

View File

@ -62,7 +62,7 @@ public final class RtspMediaPeriodTest {
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
return RtspTestUtils.newDescribeResponseWithSdpMessage(
"v=0\r\n"
+ "o=- 1606776316530225 1 IN IP4 127.0.0.1\r\n"
@ -106,4 +106,89 @@ public final class RtspMediaPeriodTest {
assertThat(refreshedSourceDurationMs.get()).isEqualTo(50_460);
}
@Test
public void prepareMediaPeriod_withWwwAuthentication_refreshesSourceInfoAndCallsOnPrepared()
throws Exception {
RtpPacketStreamDump rtpPacketStreamDump =
RtspTestUtils.readRtpPacketStreamDump("media/rtsp/aac-dump.json");
rtspServer =
new RtspServer(
new RtspServer.ResponseProvider() {
@Override
public RtspResponse getOptionsResponse() {
return new RtspResponse(
/* status= */ 200,
new RtspHeaders.Builder().add(RtspHeaders.PUBLIC, "OPTIONS, DESCRIBE").build());
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
String authorizationHeader = headers.get(RtspHeaders.AUTHORIZATION);
if (authorizationHeader == null) {
return new RtspResponse(
/* status= */ 401,
new RtspHeaders.Builder()
.add(RtspHeaders.CSEQ, headers.get(RtspHeaders.CSEQ))
.add(
RtspHeaders.WWW_AUTHENTICATE,
"Digest realm=\"LIVE555 Streaming Media\","
+ " nonce=\"0cdfe9719e7373b7d5bb2913e2115f3f\","
+ " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")
.add(RtspHeaders.WWW_AUTHENTICATE, "BASIC realm=\"WallyWorld\"")
.build());
}
if (!authorizationHeader.contains("Digest")) {
return new RtspResponse(
401,
new RtspHeaders.Builder()
.add(RtspHeaders.CSEQ, headers.get(RtspHeaders.CSEQ))
.build());
}
return RtspTestUtils.newDescribeResponseWithSdpMessage(
"v=0\r\n"
+ "o=- 1606776316530225 1 IN IP4 127.0.0.1\r\n"
+ "s=Exoplayer test\r\n"
+ "t=0 0\r\n"
// The session is 50.46s long.
+ "a=range:npt=0-50.46\r\n",
ImmutableList.of(rtpPacketStreamDump),
requestedUri);
}
});
AtomicBoolean prepareCallbackCalled = new AtomicBoolean();
AtomicLong refreshedSourceDurationMs = new AtomicLong();
mediaPeriod =
new RtspMediaPeriod(
new DefaultAllocator(/* trimOnReset= */ true, C.DEFAULT_BUFFER_SEGMENT_SIZE),
new TransferRtpDataChannelFactory(DEFAULT_TIMEOUT_MS),
RtspTestUtils.getTestUriWithUserInfo(
"username", "password", rtspServer.startAndGetPortNumber()),
/* listener= */ timing -> refreshedSourceDurationMs.set(timing.getDurationMs()),
/* userAgent= */ "ExoPlayer:RtspPeriodTest",
/* socketFactory= */ SocketFactory.getDefault(),
/* debugLoggingEnabled= */ false);
mediaPeriod.prepare(
new MediaPeriod.Callback() {
@Override
public void onPrepared(MediaPeriod mediaPeriod) {
prepareCallbackCalled.set(true);
}
@Override
public void onContinueLoadingRequested(MediaPeriod source) {
source.continueLoading(/* positionUs= */ 0);
}
},
/* positionUs= */ 0);
RobolectricUtil.runMainLooperUntil(prepareCallbackCalled::get);
mediaPeriod.release();
assertThat(refreshedSourceDurationMs.get()).isEqualTo(50_460);
}
}

View File

@ -209,7 +209,7 @@ public final class RtspPlaybackTest {
}
@Override
public RtspResponse getDescribeResponse(Uri requestedUri) {
public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
return RtspTestUtils.newDescribeResponseWithSdpMessage(
SESSION_DESCRIPTION, rtpPacketStreamDumps, requestedUri);
}

View File

@ -45,7 +45,7 @@ public final class RtspServer implements Closeable {
RtspResponse getOptionsResponse();
/** Returns an RTSP DESCRIBE {@link RtspResponse response}. */
default RtspResponse getDescribeResponse(Uri requestedUri) {
default RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
return RtspTestUtils.RTSP_ERROR_METHOD_NOT_ALLOWED;
}
@ -143,7 +143,7 @@ public final class RtspServer implements Closeable {
break;
case METHOD_DESCRIBE:
sendResponse(responseProvider.getDescribeResponse(request.uri), cSeq);
sendResponse(responseProvider.getDescribeResponse(request.uri, request.headers), cSeq);
break;
case METHOD_SETUP:

View File

@ -30,6 +30,7 @@ import java.util.List;
/* package */ final class RtspTestUtils {
private static final String TEST_BASE_URI = "rtsp://localhost:%d/test";
private static final String TEST_BASE_URI_WITH_USER_INFO = "rtsp://%s:%s@localhost:%d/test";
private static final String RTP_TIME_FORMAT = "url=rtsp://localhost/test/%s;seq=%d;rtptime=%d";
/** RTSP error Method Not Allowed (RFC2326 Section 7.1.1). */
@ -72,6 +73,14 @@ import java.util.List;
return Uri.parse(Util.formatInvariant(TEST_BASE_URI, serverRtspPortNumber));
}
/** Returns the test RTSP {@link Uri} with user info. */
public static Uri getTestUriWithUserInfo(
String username, String password, int serverRtspPortNumber) {
return Uri.parse(
Util.formatInvariant(
TEST_BASE_URI_WITH_USER_INFO, username, password, serverRtspPortNumber));
}
public static String getRtpInfoForDumps(List<RtpPacketStreamDump> rtpPacketStreamDumps) {
ArrayList<String> rtpInfos = new ArrayList<>(rtpPacketStreamDumps.size());
for (RtpPacketStreamDump rtpPacketStreamDump : rtpPacketStreamDumps) {