1
1
// Copyright (c) .NET Foundation. All rights reserved.
2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
- using System ;
5
4
using System . Threading . Tasks ;
6
5
using Xunit ;
7
6
8
7
namespace Microsoft . AspNetCore . Server . KestrelTests
9
8
{
10
9
public class BadHttpRequestTests
11
10
{
12
- [ Theory ( Skip = "This test fails intermittently and needs to be investigated." ) ]
13
- [ InlineData ( "/ HTTP/1.1\r \n \r \n " ) ]
14
- [ InlineData ( " / HTTP/1.1\r \n \r \n " ) ]
15
- [ InlineData ( " / HTTP/1.1\r \n \r \n " ) ]
16
- [ InlineData ( "GET / HTTP/1.1\r \n \r \n " ) ]
17
- [ InlineData ( "GET / HTTP/1.1\r \n \r \n " ) ]
18
- [ InlineData ( "GET HTTP/1.1\r \n \r \n " ) ]
11
+ // Don't send more data than necessary to fail, otherwise the test throws trying to
12
+ // send data after the server has already closed the connection. This would cause the
13
+ // test to fail on Windows, due to a winsock limitation: after the error when trying
14
+ // to write to the socket closed by the server, winsock disposes all resources used
15
+ // by that socket. The test then fails when we try to read the expected response
16
+ // from the server because, although it would have been buffered, it got discarded
17
+ // by winsock on the send() error.
18
+ // The solution for this is for the client to always try to receive before doing
19
+ // any sends, that way it can detect that the connection has been closed by the server
20
+ // and not try to send() on the closed connection, triggering the error that would cause
21
+ // any buffered received data to be lost.
22
+ // We do not deem necessary to mitigate this issue in TestConnection, since it would only
23
+ // be ensuring that we have a properly implemented HTTP client that can handle the
24
+ // winsock issue. There is nothing to be verified in Kestrel in this situation.
25
+ [ Theory ]
26
+ // Incomplete request lines
27
+ [ InlineData ( "G" ) ]
28
+ [ InlineData ( "GE" ) ]
29
+ [ InlineData ( "GET" ) ]
30
+ [ InlineData ( "GET " ) ]
19
31
[ InlineData ( "GET /" ) ]
20
32
[ InlineData ( "GET / " ) ]
21
33
[ InlineData ( "GET / H" ) ]
34
+ [ InlineData ( "GET / HT" ) ]
35
+ [ InlineData ( "GET / HTT" ) ]
36
+ [ InlineData ( "GET / HTTP" ) ]
37
+ [ InlineData ( "GET / HTTP/" ) ]
38
+ [ InlineData ( "GET / HTTP/1" ) ]
22
39
[ InlineData ( "GET / HTTP/1." ) ]
40
+ [ InlineData ( "GET / HTTP/1.1" ) ]
41
+ [ InlineData ( "GET / HTTP/1.1\r " ) ]
42
+ [ InlineData ( "GET / HTTP/1.0" ) ]
43
+ [ InlineData ( "GET / HTTP/1.0\r " ) ]
44
+ // Missing method
45
+ [ InlineData ( " " ) ]
46
+ // Missing second space
47
+ [ InlineData ( "/ HTTP/1.1\r \n \r \n " ) ]
23
48
[ InlineData ( "GET /\r \n " ) ]
24
- [ InlineData ( "GET / \r \n " ) ]
49
+ // Missing target
50
+ [ InlineData ( "GET " ) ]
51
+ // Missing version
52
+ [ InlineData ( "GET / \r " ) ]
53
+ // Missing CR
25
54
[ InlineData ( "GET / \n " ) ]
26
- [ InlineData ( "GET / http/1.0\r \n \r \n " ) ]
27
- [ InlineData ( "GET / http/1.1\r \n \r \n " ) ]
28
- [ InlineData ( "GET / HTTP/1.1 \r \n \r \n " ) ]
29
- [ InlineData ( "GET / HTTP/1.1a\r \n \r \n " ) ]
30
- [ InlineData ( "GET / HTTP/1.0\n \r \n " ) ]
31
- [ InlineData ( "GET / HTTP/3.0\r \n \r \n " ) ]
32
- [ InlineData ( "GET / H\r \n \r \n " ) ]
33
- [ InlineData ( "GET / HTTP/1.\r \n \r \n " ) ]
34
- [ InlineData ( "GET / hello\r \n \r \n " ) ]
35
- [ InlineData ( "GET / 8charact\r \n \r \n " ) ]
36
- public async Task TestBadRequests ( string request )
55
+ // Unrecognized HTTP version
56
+ [ InlineData ( "GET / http/1.0\r " ) ]
57
+ [ InlineData ( "GET / http/1.1\r " ) ]
58
+ [ InlineData ( "GET / HTTP/1.1 \r " ) ]
59
+ [ InlineData ( "GET / HTTP/1.1a\r " ) ]
60
+ [ InlineData ( "GET / HTTP/1.0\n \r " ) ]
61
+ [ InlineData ( "GET / HTTP/1.2\r " ) ]
62
+ [ InlineData ( "GET / HTTP/3.0\r " ) ]
63
+ [ InlineData ( "GET / H\r " ) ]
64
+ [ InlineData ( "GET / HTTP/1.\r " ) ]
65
+ [ InlineData ( "GET / hello\r " ) ]
66
+ [ InlineData ( "GET / 8charact\r " ) ]
67
+ // Missing LF
68
+ [ InlineData ( "GET / HTTP/1.0\r A" ) ]
69
+ public async Task TestBadRequestLines ( string request )
37
70
{
38
71
using ( var server = new TestServer ( context => { return Task . FromResult ( 0 ) ; } ) )
39
72
{
40
73
using ( var connection = new TestConnection ( server . Port ) )
41
74
{
42
- var receiveTask = Task . Run ( async ( ) =>
43
- {
44
- await connection . Receive (
45
- "HTTP/1.1 400 Bad Request" ,
46
- "" ) ;
47
- await connection . ReceiveStartsWith ( "Date: " ) ;
48
- await connection . ReceiveForcedEnd (
49
- "Content-Length: 0" ,
50
- "Server: Kestrel" ,
51
- "" ,
52
- "" ) ;
53
- } ) ;
54
-
55
- try
56
- {
57
- await connection . SendEnd ( request ) . ConfigureAwait ( false ) ;
58
- }
59
- catch ( Exception )
60
- {
61
- // TestConnection.SendEnd will start throwing while sending characters
62
- // in cases where the server rejects the request as soon as it
63
- // determines the request line is malformed, even though there
64
- // are more characters following.
65
- }
75
+ await connection . SendEnd ( request ) ;
76
+ await ReceiveBadRequestResponse ( connection ) ;
77
+ }
78
+ }
79
+ }
66
80
67
- await receiveTask ;
81
+ [ Theory ]
82
+ [ InlineData ( " " ) ]
83
+ [ InlineData ( "GET " ) ]
84
+ [ InlineData ( "GET / HTTP/1.2\r " ) ]
85
+ [ InlineData ( "GET / HTTP/1.0\r A" ) ]
86
+ public async Task ServerClosesConnectionAsSoonAsBadRequestLineIsDetected ( string request )
87
+ {
88
+ using ( var server = new TestServer ( context => { return Task . FromResult ( 0 ) ; } ) )
89
+ {
90
+ using ( var connection = new TestConnection ( server . Port ) )
91
+ {
92
+ await connection . Send ( request ) ;
93
+ await ReceiveBadRequestResponse ( connection ) ;
68
94
}
69
95
}
70
96
}
97
+
98
+ private async Task ReceiveBadRequestResponse ( TestConnection connection )
99
+ {
100
+ await connection . Receive (
101
+ "HTTP/1.1 400 Bad Request" ,
102
+ "" ) ;
103
+ await connection . Receive (
104
+ "Connection: close" ,
105
+ "" ) ;
106
+ await connection . ReceiveStartsWith ( "Date: " ) ;
107
+ await connection . ReceiveEnd (
108
+ "Content-Length: 0" ,
109
+ "Server: Kestrel" ,
110
+ "" ,
111
+ "" ) ;
112
+ }
71
113
}
72
114
}
0 commit comments