6
6
using System . Linq ;
7
7
using System . Net ;
8
8
using System . Net . Http ;
9
+ using System . Net . Sockets ;
9
10
using System . Text ;
10
11
using System . Threading . Tasks ;
11
12
using Microsoft . AspNetCore . InternalTesting ;
@@ -207,20 +208,66 @@ public async Task ResponseHeaders_11HeadRequestStatusCodeWithoutBody_NoContentLe
207
208
}
208
209
209
210
[ ConditionalFact ]
210
- public async Task ResponseHeaders_HTTP10KeepAliveRequest_Gets11Close ( )
211
+ public async Task ResponseHeaders_HTTP10KeepAliveRequest_KeepAliveHeader_Gets11NoClose ( )
212
+ {
213
+ string address ;
214
+ using ( var server = Utilities . CreateHttpServer ( out address ) )
215
+ {
216
+ // Track the number of times ConnectCallback is invoked to ensure the underlying socket wasn't closed.
217
+ int connectCallbackInvocations = 0 ;
218
+ var handler = new SocketsHttpHandler ( ) ;
219
+ handler . ConnectCallback = ( context , cancellationToken ) =>
220
+ {
221
+ Interlocked . Increment ( ref connectCallbackInvocations ) ;
222
+ return ConnectCallback ( context , cancellationToken ) ;
223
+ } ;
224
+
225
+ using ( var client = new HttpClient ( handler ) )
226
+ {
227
+ // Send the first request
228
+ Task < HttpResponseMessage > responseTask = SendRequestAsync ( address , usehttp11 : false , sendKeepAlive : true , httpClient : client ) ;
229
+ var context = await server . AcceptAsync ( Utilities . DefaultTimeout ) . Before ( responseTask ) ;
230
+ context . Dispose ( ) ;
231
+
232
+ HttpResponseMessage response = await responseTask ;
233
+ response . EnsureSuccessStatusCode ( ) ;
234
+ Assert . Equal ( new Version ( 1 , 1 ) , response . Version ) ;
235
+ Assert . Null ( response . Headers . ConnectionClose ) ;
236
+
237
+ // Send the second request
238
+ responseTask = SendRequestAsync ( address , usehttp11 : false , sendKeepAlive : true , httpClient : client ) ;
239
+ context = await server . AcceptAsync ( Utilities . DefaultTimeout ) . Before ( responseTask ) ;
240
+ context . Dispose ( ) ;
241
+
242
+ response = await responseTask ;
243
+ response . EnsureSuccessStatusCode ( ) ;
244
+ Assert . Equal ( new Version ( 1 , 1 ) , response . Version ) ;
245
+ Assert . Null ( response . Headers . ConnectionClose ) ;
246
+ }
247
+
248
+ // Verify that ConnectCallback was only called once
249
+ Assert . Equal ( 1 , connectCallbackInvocations ) ;
250
+ }
251
+ }
252
+
253
+ [ ConditionalFact ]
254
+ public async Task ResponseHeaders_HTTP10KeepAliveRequest_ChunkedTransferEncoding_Gets11Close ( )
211
255
{
212
256
string address ;
213
257
using ( var server = Utilities . CreateHttpServer ( out address ) )
214
258
{
215
- // Http.Sys does not support 1.0 keep-alives.
216
259
Task < HttpResponseMessage > responseTask = SendRequestAsync ( address , usehttp11 : false , sendKeepAlive : true ) ;
217
260
218
261
var context = await server . AcceptAsync ( Utilities . DefaultTimeout ) . Before ( responseTask ) ;
262
+ context . Response . Headers [ "Transfer-Encoding" ] = new string [ ] { "chunked" } ;
263
+ var responseBytes = Encoding . ASCII . GetBytes ( "10\r \n Manually Chunked\r \n 0\r \n \r \n " ) ;
264
+ await context . Response . Body . WriteAsync ( responseBytes , 0 , responseBytes . Length ) ;
219
265
context . Dispose ( ) ;
220
266
221
267
HttpResponseMessage response = await responseTask ;
222
268
response . EnsureSuccessStatusCode ( ) ;
223
269
Assert . Equal ( new Version ( 1 , 1 ) , response . Version ) ;
270
+ Assert . True ( response . Headers . TransferEncodingChunked . HasValue , "Chunked" ) ;
224
271
Assert . True ( response . Headers . ConnectionClose . Value ) ;
225
272
}
226
273
}
@@ -289,8 +336,9 @@ public async Task AddingControlCharactersToHeadersThrows(string key, string valu
289
336
}
290
337
}
291
338
292
- private async Task < HttpResponseMessage > SendRequestAsync ( string uri , bool usehttp11 = true , bool sendKeepAlive = false )
339
+ private async Task < HttpResponseMessage > SendRequestAsync ( string uri , bool usehttp11 = true , bool sendKeepAlive = false , HttpClient httpClient = null )
293
340
{
341
+ httpClient ??= _client ;
294
342
var request = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
295
343
if ( ! usehttp11 )
296
344
{
@@ -300,7 +348,7 @@ private async Task<HttpResponseMessage> SendRequestAsync(string uri, bool usehtt
300
348
{
301
349
request . Headers . Add ( "Connection" , "Keep-Alive" ) ;
302
350
}
303
- return await _client . SendAsync ( request ) ;
351
+ return await httpClient . SendAsync ( request ) ;
304
352
}
305
353
306
354
private async Task < HttpResponseMessage > SendHeadRequestAsync ( string uri , bool usehttp11 = true )
@@ -312,4 +360,19 @@ private async Task<HttpResponseMessage> SendHeadRequestAsync(string uri, bool us
312
360
}
313
361
return await _client . SendAsync ( request ) ;
314
362
}
363
+
364
+ private static async ValueTask < Stream > ConnectCallback ( SocketsHttpConnectionContext connectContext , CancellationToken ct )
365
+ {
366
+ var s = new Socket ( SocketType . Stream , ProtocolType . Tcp ) { NoDelay = true } ;
367
+ try
368
+ {
369
+ await s . ConnectAsync ( connectContext . DnsEndPoint , ct ) ;
370
+ return new NetworkStream ( s , ownsSocket : true ) ;
371
+ }
372
+ catch
373
+ {
374
+ s . Dispose ( ) ;
375
+ throw ;
376
+ }
377
+ }
315
378
}
0 commit comments