From d5e24ed76f6c987a88f556f567924f4165b2276c Mon Sep 17 00:00:00 2001 From: Aaron Clauson Date: Mon, 1 Jun 2026 14:36:01 +0100 Subject: [PATCH] Add unit tests for RTSPHeader.SplitHeaders line folding SplitHeaders performs RTSP header line-folding (collapsing continuation lines that begin with whitespace) and normalisation of malformed carriage-return line endings, but had no direct test coverage; it was only exercised indirectly via well-formed round-trip messages. These characterisation tests lock in the existing behaviour: - basic CRLF-separated splitting - continuation line folded with a leading space - continuation line folded with a leading tab - lone "\r " (CR + space) treated as a line break Verified to pass against both the current regex-based implementation and the span-based rewrite in #1654, confirming the two are equivalent. Co-Authored-By: Claude Opus 4.8 --- test/unit/net/RTSP/RTSPHeaderUnitTest.cs | 112 +++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 test/unit/net/RTSP/RTSPHeaderUnitTest.cs diff --git a/test/unit/net/RTSP/RTSPHeaderUnitTest.cs b/test/unit/net/RTSP/RTSPHeaderUnitTest.cs new file mode 100644 index 000000000..8a398da61 --- /dev/null +++ b/test/unit/net/RTSP/RTSPHeaderUnitTest.cs @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Filename: RTSPHeaderUnitTest.cs +// +// Description: Unit tests for the RTSPHeader class, in particular the header +// splitting/line-folding logic in SplitHeaders. +// +// Author(s): +// Aaron Clauson +// +// History: +// 01 Jun 2026 Aaron Clauson Created to characterise SplitHeaders line folding. +// +// License: +// BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file. +//----------------------------------------------------------------------------- + +using Microsoft.Extensions.Logging; +using SIPSorcery.UnitTests; +using Xunit; + +namespace SIPSorcery.Net.UnitTests +{ + [Trait("Category", "unit")] + public class RTSPHeaderUnitTest + { + private const string CRLF = "\r\n"; + + private Microsoft.Extensions.Logging.ILogger logger = null; + + public RTSPHeaderUnitTest(Xunit.Abstractions.ITestOutputHelper output) + { + logger = SIPSorcery.UnitTests.TestLogHelper.InitTestLogger(output); + } + + /// + /// Tests that a well formed block of CRLF separated headers is split into one + /// entry per header line. + /// + [Fact] + public void SplitHeadersBasicTest() + { + logger.LogDebug("--> {MethodName}", TestHelper.GetCurrentMethodName()); + logger.BeginScope(TestHelper.GetCurrentMethodName()); + + string message = "CSeq: 1" + CRLF + "Session: abc123" + CRLF + "Content-Length: 0"; + + string[] headers = RTSPHeader.SplitHeaders(message); + + Assert.Equal(3, headers.Length); + Assert.Equal("CSeq: 1", headers[0]); + Assert.Equal("Session: abc123", headers[1]); + Assert.Equal("Content-Length: 0", headers[2]); + } + + /// + /// Tests that a header value folded across multiple lines (a continuation line begins + /// with whitespace) is collapsed back onto a single line separated by a single space. + /// + [Fact] + public void SplitHeadersFoldedWithSpaceTest() + { + logger.LogDebug("--> {MethodName}", TestHelper.GetCurrentMethodName()); + logger.BeginScope(TestHelper.GetCurrentMethodName()); + + string message = "Accept: application/sdp," + CRLF + " application/rtsl" + CRLF + "CSeq: 2"; + + string[] headers = RTSPHeader.SplitHeaders(message); + + Assert.Equal(2, headers.Length); + Assert.Equal("Accept: application/sdp, application/rtsl", headers[0]); + Assert.Equal("CSeq: 2", headers[1]); + } + + /// + /// Tests that a continuation line indented with a tab is also folded back onto the + /// previous line. + /// + [Fact] + public void SplitHeadersFoldedWithTabTest() + { + logger.LogDebug("--> {MethodName}", TestHelper.GetCurrentMethodName()); + logger.BeginScope(TestHelper.GetCurrentMethodName()); + + string message = "Accept: a," + CRLF + "\tb" + CRLF + "CSeq: 3"; + + string[] headers = RTSPHeader.SplitHeaders(message); + + Assert.Equal(2, headers.Length); + Assert.Equal("Accept: a, b", headers[0]); + Assert.Equal("CSeq: 3", headers[1]); + } + + /// + /// Tests that a malformed line ending of a lone carriage return followed by a space + /// (some user agents don't get the \r\n right) is treated as a proper line break. + /// + [Fact] + public void SplitHeadersMalformedCarriageReturnTest() + { + logger.LogDebug("--> {MethodName}", TestHelper.GetCurrentMethodName()); + logger.BeginScope(TestHelper.GetCurrentMethodName()); + + string message = "CSeq: 4\r Session: xyz"; + + string[] headers = RTSPHeader.SplitHeaders(message); + + Assert.Equal(2, headers.Length); + Assert.Equal("CSeq: 4", headers[0]); + Assert.Equal("Session: xyz", headers[1]); + } + } +}