Skip to content

Commit 6f49ffa

Browse files
sipsorceryclaude
andauthored
Add unit tests for RTSPHeader.SplitHeaders line folding (#1657)
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 <noreply@anthropic.com>
1 parent 1bad60a commit 6f49ffa

1 file changed

Lines changed: 112 additions & 0 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//-----------------------------------------------------------------------------
2+
// Filename: RTSPHeaderUnitTest.cs
3+
//
4+
// Description: Unit tests for the RTSPHeader class, in particular the header
5+
// splitting/line-folding logic in SplitHeaders.
6+
//
7+
// Author(s):
8+
// Aaron Clauson
9+
//
10+
// History:
11+
// 01 Jun 2026 Aaron Clauson Created to characterise SplitHeaders line folding.
12+
//
13+
// License:
14+
// BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file.
15+
//-----------------------------------------------------------------------------
16+
17+
using Microsoft.Extensions.Logging;
18+
using SIPSorcery.UnitTests;
19+
using Xunit;
20+
21+
namespace SIPSorcery.Net.UnitTests
22+
{
23+
[Trait("Category", "unit")]
24+
public class RTSPHeaderUnitTest
25+
{
26+
private const string CRLF = "\r\n";
27+
28+
private Microsoft.Extensions.Logging.ILogger logger = null;
29+
30+
public RTSPHeaderUnitTest(Xunit.Abstractions.ITestOutputHelper output)
31+
{
32+
logger = SIPSorcery.UnitTests.TestLogHelper.InitTestLogger(output);
33+
}
34+
35+
/// <summary>
36+
/// Tests that a well formed block of CRLF separated headers is split into one
37+
/// entry per header line.
38+
/// </summary>
39+
[Fact]
40+
public void SplitHeadersBasicTest()
41+
{
42+
logger.LogDebug("--> {MethodName}", TestHelper.GetCurrentMethodName());
43+
logger.BeginScope(TestHelper.GetCurrentMethodName());
44+
45+
string message = "CSeq: 1" + CRLF + "Session: abc123" + CRLF + "Content-Length: 0";
46+
47+
string[] headers = RTSPHeader.SplitHeaders(message);
48+
49+
Assert.Equal(3, headers.Length);
50+
Assert.Equal("CSeq: 1", headers[0]);
51+
Assert.Equal("Session: abc123", headers[1]);
52+
Assert.Equal("Content-Length: 0", headers[2]);
53+
}
54+
55+
/// <summary>
56+
/// Tests that a header value folded across multiple lines (a continuation line begins
57+
/// with whitespace) is collapsed back onto a single line separated by a single space.
58+
/// </summary>
59+
[Fact]
60+
public void SplitHeadersFoldedWithSpaceTest()
61+
{
62+
logger.LogDebug("--> {MethodName}", TestHelper.GetCurrentMethodName());
63+
logger.BeginScope(TestHelper.GetCurrentMethodName());
64+
65+
string message = "Accept: application/sdp," + CRLF + " application/rtsl" + CRLF + "CSeq: 2";
66+
67+
string[] headers = RTSPHeader.SplitHeaders(message);
68+
69+
Assert.Equal(2, headers.Length);
70+
Assert.Equal("Accept: application/sdp, application/rtsl", headers[0]);
71+
Assert.Equal("CSeq: 2", headers[1]);
72+
}
73+
74+
/// <summary>
75+
/// Tests that a continuation line indented with a tab is also folded back onto the
76+
/// previous line.
77+
/// </summary>
78+
[Fact]
79+
public void SplitHeadersFoldedWithTabTest()
80+
{
81+
logger.LogDebug("--> {MethodName}", TestHelper.GetCurrentMethodName());
82+
logger.BeginScope(TestHelper.GetCurrentMethodName());
83+
84+
string message = "Accept: a," + CRLF + "\tb" + CRLF + "CSeq: 3";
85+
86+
string[] headers = RTSPHeader.SplitHeaders(message);
87+
88+
Assert.Equal(2, headers.Length);
89+
Assert.Equal("Accept: a, b", headers[0]);
90+
Assert.Equal("CSeq: 3", headers[1]);
91+
}
92+
93+
/// <summary>
94+
/// Tests that a malformed line ending of a lone carriage return followed by a space
95+
/// (some user agents don't get the \r\n right) is treated as a proper line break.
96+
/// </summary>
97+
[Fact]
98+
public void SplitHeadersMalformedCarriageReturnTest()
99+
{
100+
logger.LogDebug("--> {MethodName}", TestHelper.GetCurrentMethodName());
101+
logger.BeginScope(TestHelper.GetCurrentMethodName());
102+
103+
string message = "CSeq: 4\r Session: xyz";
104+
105+
string[] headers = RTSPHeader.SplitHeaders(message);
106+
107+
Assert.Equal(2, headers.Length);
108+
Assert.Equal("CSeq: 4", headers[0]);
109+
Assert.Equal("Session: xyz", headers[1]);
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)