@@ -42,28 +42,37 @@ public class DummyHttpServer : IDisposable
4242 private int _port = 29743 ;
4343 private readonly TimeSpan ? _withStartDelay ;
4444
45+ /// <summary>
46+ /// Initializes a configurable in-process dummy HTTP server used for testing endpoints.
47+ /// </summary>
48+ /// <param name="withTokenAuth">If true, enable JWT bearer authentication and authorization.</param>
49+ /// <param name="withBasicAuth">If true, enable basic authentication behavior in the test endpoint.</param>
50+ /// <param name="withRetriableError">If true, configure the test endpoint to produce retriable error responses.</param>
51+ /// <param name="withErrorMessage">If true, include error messages in test error responses.</param>
52+ /// <param name="withStartDelay">Optional delay applied when starting the server.</param>
53+ /// <param name="requireClientCert">If true, require client TLS certificates for HTTPS connections.</param>
4554 public DummyHttpServer ( bool withTokenAuth = false , bool withBasicAuth = false , bool withRetriableError = false ,
46- bool withErrorMessage = false , TimeSpan ? withStartDelay = null , bool requireClientCert = false )
55+ bool withErrorMessage = false , TimeSpan ? withStartDelay = null , bool requireClientCert = false )
4756 {
4857 var bld = WebApplication . CreateBuilder ( ) ;
4958
5059 bld . Services . AddLogging ( builder =>
5160 {
5261 builder . AddFilter ( "Microsoft" , LogLevel . Warning )
53- . AddFilter ( "System" , LogLevel . Warning )
54- . AddConsole ( ) ;
62+ . AddFilter ( "System" , LogLevel . Warning )
63+ . AddConsole ( ) ;
5564 } ) ;
5665
57- IlpEndpoint . WithTokenAuth = withTokenAuth ;
58- IlpEndpoint . WithBasicAuth = withBasicAuth ;
66+ IlpEndpoint . WithTokenAuth = withTokenAuth ;
67+ IlpEndpoint . WithBasicAuth = withBasicAuth ;
5968 IlpEndpoint . WithRetriableError = withRetriableError ;
60- IlpEndpoint . WithErrorMessage = withErrorMessage ;
69+ IlpEndpoint . WithErrorMessage = withErrorMessage ;
6170 _withStartDelay = withStartDelay ;
6271
6372 if ( withTokenAuth )
6473 {
6574 bld . Services . AddAuthenticationJwtBearer ( s => s . SigningKey = SigningKey )
66- . AddAuthorization ( ) ;
75+ . AddAuthorization ( ) ;
6776 }
6877
6978
@@ -83,7 +92,7 @@ public DummyHttpServer(bool withTokenAuth = false, bool withBasicAuth = false, b
8392
8493 o . Limits . MaxRequestBodySize = 1073741824 ;
8594 o . ListenLocalhost ( 29474 ,
86- options => { options . UseHttps ( ) ; } ) ;
95+ options => { options . UseHttps ( ) ; } ) ;
8796 o . ListenLocalhost ( 29473 ) ;
8897 } ) ;
8998
@@ -108,26 +117,43 @@ public void Dispose()
108117 _app . StopAsync ( ) . Wait ( ) ;
109118 }
110119
120+ /// <summary>
121+ /// Clears the in-memory receive buffers and resets the endpoint error state and counter.
122+ /// </summary>
123+ /// <remarks>
124+ /// Empties IlpEndpoint.ReceiveBuffer and IlpEndpoint.ReceiveBytes, sets IlpEndpoint.LastError to null,
125+ /// and sets IlpEndpoint.Counter to zero.
126+ /// </remarks>
111127 public void Clear ( )
112128 {
113129 IlpEndpoint . ReceiveBuffer . Clear ( ) ;
114130 IlpEndpoint . ReceiveBytes . Clear ( ) ;
115131 IlpEndpoint . LastError = null ;
116- IlpEndpoint . Counter = 0 ;
132+ IlpEndpoint . Counter = 0 ;
117133 }
118134
135+ /// <summary>
136+ /// Starts the HTTP server on the specified port and configures the supported protocol versions.
137+ /// </summary>
138+ /// <param name="port">Port to listen on (defaults to 29743).</param>
139+ /// <param name="versions">Array of supported protocol versions; defaults to {1, 2, 3} when null.</param>
140+ /// <returns>A task that completes after any configured startup delay has elapsed and the server's background run task has been initiated.</returns>
119141 public async Task StartAsync ( int port = 29743 , int [ ] ? versions = null )
120142 {
121143 if ( _withStartDelay . HasValue )
122144 {
123145 await Task . Delay ( _withStartDelay . Value ) ;
124146 }
125- versions ??= new [ ] { 1 , 2 , } ;
126- SettingsEndpoint . Versions = versions ;
127- _port = port ;
128- _app . RunAsync ( $ "http://localhost:{ port } ") ;
147+
148+ versions ??= new [ ] { 1 , 2 , 3 , } ;
149+ SettingsEndpoint . Versions = versions ;
150+ _port = port ;
151+ _ = _app . RunAsync ( $ "http://localhost:{ port } ") ;
129152 }
130153
154+ /// <summary>
155+ /// Starts the web application and listens for HTTP requests on http://localhost:{_port}.
156+ /// </summary>
131157 public async Task RunAsync ( )
132158 {
133159 await _app . RunAsync ( $ "http://localhost:{ _port } ") ;
@@ -138,12 +164,20 @@ public async Task StopAsync()
138164 await _app . StopAsync ( ) ;
139165 }
140166
167+ /// <summary>
168+ /// Gets the server's in-memory text buffer of received data.
169+ /// </summary>
170+ /// <returns>The mutable <see cref="StringBuilder"/> containing the accumulated received text; modifying it updates the server's buffer.</returns>
141171 public StringBuilder GetReceiveBuffer ( )
142172 {
143173 return IlpEndpoint . ReceiveBuffer ;
144174 }
145175
146- public List < byte > GetReceiveBytes ( )
176+ /// <summary>
177+ /// Gets the in-memory list of bytes received by the ILP endpoint.
178+ /// </summary>
179+ /// <returns>The mutable list of bytes received by the endpoint.</returns>
180+ public List < byte > GetReceivedBytes ( )
147181 {
148182 return IlpEndpoint . ReceiveBytes ;
149183 }
@@ -160,14 +194,18 @@ public async Task<bool> Healthcheck()
160194 }
161195
162196
197+ /// <summary>
198+ /// Generates a JWT for the test server when the provided credentials match the server's static username and password.
199+ /// </summary>
200+ /// <returns>The JWT string when credentials are valid; <c>null</c> otherwise. The issued token is valid for one day.</returns>
163201 public string ? GetJwtToken ( string username , string password )
164202 {
165203 if ( username == Username && password == Password )
166204 {
167205 var jwtToken = JwtBearer . CreateToken ( o =>
168206 {
169207 o . SigningKey = SigningKey ;
170- o . ExpireAt = DateTime . UtcNow . AddDays ( 1 ) ;
208+ o . ExpireAt = DateTime . UtcNow . AddDays ( 1 ) ;
171209 } ) ;
172210 return jwtToken ;
173211 }
@@ -180,87 +218,89 @@ public int GetCounter()
180218 return IlpEndpoint . Counter ;
181219 }
182220
221+ /// <summary>
222+ /// Produces a human-readable string representation of the server's received-bytes buffer, interpreting embedded markers and formatting arrays and numeric values.
223+ /// </summary>
224+ /// <returns>The formatted textual representation of the received bytes buffer.</returns>
225+ /// <exception cref="NotImplementedException">Thrown when the buffer contains an unsupported type code.</exception>
183226 public string PrintBuffer ( )
184227 {
185- var bytes = GetReceiveBytes ( ) . ToArray ( ) ;
186- var sb = new StringBuilder ( ) ;
228+ var bytes = GetReceivedBytes ( ) . ToArray ( ) ;
229+ var sb = new StringBuilder ( ) ;
187230 var lastAppend = 0 ;
188231
189232 var i = 0 ;
190233 for ( ; i < bytes . Length ; i ++ )
191234 {
192- if ( bytes [ i ] == ( byte ) '=' )
235+ if ( bytes [ i ] == ( byte ) '=' && i > 0 && bytes [ i - 1 ] == ( byte ) '=' )
193236 {
194- if ( bytes [ i - 1 ] == ( byte ) '=' )
237+ sb . Append ( Encoding . UTF8 . GetString ( bytes , lastAppend , i + 1 - lastAppend ) ) ;
238+ switch ( bytes [ ++ i ] )
195239 {
196- sb . Append ( Encoding . UTF8 . GetString ( bytes , lastAppend , i + 1 - lastAppend ) ) ;
197- switch ( bytes [ ++ i ] )
198- {
199- case 14 :
200- sb . Append ( "ARRAY<" ) ;
201- var type = bytes [ ++ i ] ;
240+ case 14 :
241+ sb . Append ( "ARRAY<" ) ;
242+ var type = bytes [ ++ i ] ;
202243
203- Debug . Assert ( type == 10 ) ;
204- var dims = bytes [ ++ i ] ;
244+ Debug . Assert ( type == 10 ) ;
245+ var dims = bytes [ ++ i ] ;
205246
206- ++ i ;
247+ ++ i ;
207248
208- long length = 0 ;
209- for ( var j = 0 ; j < dims ; j ++ )
249+ long length = 0 ;
250+ for ( var j = 0 ; j < dims ; j ++ )
251+ {
252+ var lengthBytes = bytes . AsSpan ( ) [ i ..( i + 4 ) ] ;
253+ var lengthValue = MemoryMarshal . Cast < byte , uint > ( lengthBytes ) [ 0 ] ;
254+ if ( length == 0 )
210255 {
211- var lengthBytes = bytes . AsSpan ( ) [ i ..( i + 4 ) ] ;
212- var lengthValue = MemoryMarshal . Cast < byte , uint > ( lengthBytes ) [ 0 ] ;
213- if ( length == 0 )
214- {
215- length = lengthValue ;
216- }
217- else
218- {
219- length *= lengthValue ;
220- }
221-
222- sb . Append ( lengthValue ) ;
223- sb . Append ( ',' ) ;
224- i += 4 ;
256+ length = lengthValue ;
225257 }
226-
227- sb . Remove ( sb . Length - 1 , 1 ) ;
228- sb . Append ( '>' ) ;
229-
230- var doubleBytes =
231- MemoryMarshal . Cast < byte , double > ( bytes . AsSpan ( ) . Slice ( i , ( int ) ( length * 8 ) ) ) ;
232-
233-
234- sb . Append ( '[' ) ;
235- for ( var j = 0 ; j < length ; j ++ )
258+ else
236259 {
237- sb . Append ( doubleBytes [ j ] ) ;
238- sb . Append ( ',' ) ;
260+ length *= lengthValue ;
239261 }
240262
241- sb . Remove ( sb . Length - 1 , 1 ) ;
242- sb . Append ( ']' ) ;
243-
244- i += ( int ) ( length * 8 ) ;
245- i -- ;
246- break ;
247- case 16 :
248- sb . Remove ( sb . Length - 1 , 1 ) ;
249- var doubleValue = MemoryMarshal . Cast < byte , double > ( bytes . AsSpan ( ) . Slice ( ++ i , 8 ) ) ;
250- sb . Append ( doubleValue [ 0 ] . ToString ( CultureInfo . InvariantCulture ) ) ;
251- i += 8 ;
252- i -- ;
253- break ;
254- default :
255- throw new NotImplementedException ( ) ;
256- }
257-
258- lastAppend = i + 1 ;
263+ sb . Append ( lengthValue ) ;
264+ sb . Append ( ',' ) ;
265+ i += 4 ;
266+ }
267+
268+ sb . Remove ( sb . Length - 1 , 1 ) ;
269+ sb . Append ( '>' ) ;
270+
271+ var doubleBytes =
272+ MemoryMarshal . Cast < byte , double > ( bytes . AsSpan ( ) . Slice ( i , ( int ) ( length * 8 ) ) ) ;
273+
274+
275+ sb . Append ( '[' ) ;
276+ for ( var j = 0 ; j < length ; j ++ )
277+ {
278+ sb . Append ( doubleBytes [ j ] . ToString ( CultureInfo . InvariantCulture ) ) ;
279+ sb . Append ( ',' ) ;
280+ }
281+
282+ sb . Remove ( sb . Length - 1 , 1 ) ;
283+ sb . Append ( ']' ) ;
284+
285+ i += ( int ) ( length * 8 ) ;
286+ i -- ;
287+ break ;
288+ case 16 :
289+ sb . Remove ( sb . Length - 1 , 1 ) ;
290+ var doubleValue = MemoryMarshal . Cast < byte , double > ( bytes . AsSpan ( ) . Slice ( ++ i , 8 ) ) ;
291+ sb . Append ( doubleValue [ 0 ] . ToString ( CultureInfo . InvariantCulture ) ) ;
292+ i += 8 ;
293+ i -- ;
294+ break ;
295+ default :
296+ throw new NotImplementedException ( $ "Type { bytes [ i ] } not implemented") ;
259297 }
298+
299+ lastAppend = i + 1 ;
260300 }
261301 }
262302
263303 sb . Append ( Encoding . UTF8 . GetString ( bytes , lastAppend , i - lastAppend ) ) ;
264304 return sb . ToString ( ) ;
265305 }
266- }
306+ }
0 commit comments