1
- // Copyright (c) .NET Foundation. All rights reserved.
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
4
using System ;
@@ -18,10 +18,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
18
18
internal class AddressBinder
19
19
{
20
20
public static async Task BindAsync ( IServerAddressesFeature addresses ,
21
- List < ListenOptions > listenOptions ,
21
+ KestrelServerOptions serverOptions ,
22
22
ILogger logger ,
23
+ IDefaultHttpsProvider defaultHttpsProvider ,
23
24
Func < ListenOptions , Task > createBinding )
24
25
{
26
+ var listenOptions = serverOptions . ListenOptions ;
25
27
var strategy = CreateStrategy (
26
28
listenOptions . ToArray ( ) ,
27
29
addresses . Addresses . ToArray ( ) ,
@@ -31,7 +33,9 @@ public static async Task BindAsync(IServerAddressesFeature addresses,
31
33
{
32
34
Addresses = addresses . Addresses ,
33
35
ListenOptions = listenOptions ,
36
+ ServerOptions = serverOptions ,
34
37
Logger = logger ,
38
+ DefaultHttpsProvider = defaultHttpsProvider ?? UnconfiguredDefaultHttpsProvider . Instance ,
35
39
CreateBinding = createBinding
36
40
} ;
37
41
@@ -47,7 +51,9 @@ private class AddressBindContext
47
51
{
48
52
public ICollection < string > Addresses { get ; set ; }
49
53
public List < ListenOptions > ListenOptions { get ; set ; }
54
+ public KestrelServerOptions ServerOptions { get ; set ; }
50
55
public ILogger Logger { get ; set ; }
56
+ public IDefaultHttpsProvider DefaultHttpsProvider { get ; set ; }
51
57
52
58
public Func < ListenOptions , Task > CreateBinding { get ; set ; }
53
59
}
@@ -120,7 +126,7 @@ private static async Task BindEndpointAsync(ListenOptions endpoint, AddressBindC
120
126
context . ListenOptions . Add ( endpoint ) ;
121
127
}
122
128
123
- private static async Task BindLocalhostAsync ( ServerAddress address , AddressBindContext context )
129
+ private static async Task BindLocalhostAsync ( ServerAddress address , AddressBindContext context , bool https )
124
130
{
125
131
if ( address . Port == 0 )
126
132
{
@@ -131,7 +137,14 @@ private static async Task BindLocalhostAsync(ServerAddress address, AddressBindC
131
137
132
138
try
133
139
{
134
- await BindEndpointAsync ( new IPEndPoint ( IPAddress . Loopback , address . Port ) , context ) . ConfigureAwait ( false ) ;
140
+ var options = new ListenOptions ( new IPEndPoint ( IPAddress . Loopback , address . Port ) ) ;
141
+ await BindEndpointAsync ( options , context ) . ConfigureAwait ( false ) ;
142
+
143
+ if ( https )
144
+ {
145
+ options . KestrelServerOptions = context . ServerOptions ;
146
+ context . DefaultHttpsProvider . ConfigureHttps ( options ) ;
147
+ }
135
148
}
136
149
catch ( Exception ex ) when ( ! ( ex is IOException ) )
137
150
{
@@ -141,7 +154,14 @@ private static async Task BindLocalhostAsync(ServerAddress address, AddressBindC
141
154
142
155
try
143
156
{
144
- await BindEndpointAsync ( new IPEndPoint ( IPAddress . IPv6Loopback , address . Port ) , context ) . ConfigureAwait ( false ) ;
157
+ var options = new ListenOptions ( new IPEndPoint ( IPAddress . IPv6Loopback , address . Port ) ) ;
158
+ await BindEndpointAsync ( options , context ) . ConfigureAwait ( false ) ;
159
+
160
+ if ( https )
161
+ {
162
+ options . KestrelServerOptions = context . ServerOptions ;
163
+ context . DefaultHttpsProvider . ConfigureHttps ( options ) ;
164
+ }
145
165
}
146
166
catch ( Exception ex ) when ( ! ( ex is IOException ) )
147
167
{
@@ -162,10 +182,11 @@ private static async Task BindLocalhostAsync(ServerAddress address, AddressBindC
162
182
private static async Task BindAddressAsync ( string address , AddressBindContext context )
163
183
{
164
184
var parsedAddress = ServerAddress . FromUrl ( address ) ;
185
+ var https = false ;
165
186
166
187
if ( parsedAddress . Scheme . Equals ( "https" , StringComparison . OrdinalIgnoreCase ) )
167
188
{
168
- throw new InvalidOperationException ( CoreStrings . FormatConfigureHttpsFromMethodCall ( $ " { nameof ( KestrelServerOptions ) } . { nameof ( KestrelServerOptions . Listen ) } ()" ) ) ;
189
+ https = true ;
169
190
}
170
191
else if ( ! parsedAddress . Scheme . Equals ( "http" , StringComparison . OrdinalIgnoreCase ) )
171
192
{
@@ -177,20 +198,20 @@ private static async Task BindAddressAsync(string address, AddressBindContext co
177
198
throw new InvalidOperationException ( CoreStrings . FormatConfigurePathBaseFromMethodCall ( $ "{ nameof ( IApplicationBuilder ) } .UsePathBase()") ) ;
178
199
}
179
200
201
+ ListenOptions options = null ;
180
202
if ( parsedAddress . IsUnixPipe )
181
203
{
182
- var endPoint = new ListenOptions ( parsedAddress . UnixPipePath ) ;
183
- await BindEndpointAsync ( endPoint , context ) . ConfigureAwait ( false ) ;
184
- context . Addresses . Add ( endPoint . GetDisplayName ( ) ) ;
204
+ options = new ListenOptions ( parsedAddress . UnixPipePath ) ;
205
+ await BindEndpointAsync ( options , context ) . ConfigureAwait ( false ) ;
206
+ context . Addresses . Add ( options . GetDisplayName ( ) ) ;
185
207
}
186
208
else if ( string . Equals ( parsedAddress . Host , "localhost" , StringComparison . OrdinalIgnoreCase ) )
187
209
{
188
210
// "localhost" for both IPv4 and IPv6 can't be represented as an IPEndPoint.
189
- await BindLocalhostAsync ( parsedAddress , context ) . ConfigureAwait ( false ) ;
211
+ await BindLocalhostAsync ( parsedAddress , context , https ) . ConfigureAwait ( false ) ;
190
212
}
191
213
else
192
214
{
193
- ListenOptions options ;
194
215
if ( TryCreateIPEndPoint ( parsedAddress , out var endpoint ) )
195
216
{
196
217
options = new ListenOptions ( endpoint ) ;
@@ -216,6 +237,12 @@ private static async Task BindAddressAsync(string address, AddressBindContext co
216
237
217
238
context . Addresses . Add ( options . GetDisplayName ( ) ) ;
218
239
}
240
+
241
+ if ( https && options != null )
242
+ {
243
+ options . KestrelServerOptions = context . ServerOptions ;
244
+ context . DefaultHttpsProvider . ConfigureHttps ( options ) ;
245
+ }
219
246
}
220
247
221
248
private interface IStrategy
@@ -229,7 +256,7 @@ public async Task BindAsync(AddressBindContext context)
229
256
{
230
257
context . Logger . LogDebug ( CoreStrings . BindingToDefaultAddress , Constants . DefaultServerAddress ) ;
231
258
232
- await BindLocalhostAsync ( ServerAddress . FromUrl ( Constants . DefaultServerAddress ) , context ) . ConfigureAwait ( false ) ;
259
+ await BindLocalhostAsync ( ServerAddress . FromUrl ( Constants . DefaultServerAddress ) , context , https : false ) . ConfigureAwait ( false ) ;
233
260
}
234
261
}
235
262
@@ -305,5 +332,22 @@ public virtual async Task BindAsync(AddressBindContext context)
305
332
}
306
333
}
307
334
}
335
+
336
+ private class UnconfiguredDefaultHttpsProvider : IDefaultHttpsProvider
337
+ {
338
+ public static readonly UnconfiguredDefaultHttpsProvider Instance = new UnconfiguredDefaultHttpsProvider ( ) ;
339
+
340
+ private UnconfiguredDefaultHttpsProvider ( )
341
+ {
342
+ }
343
+
344
+ public void ConfigureHttps ( ListenOptions listenOptions )
345
+ {
346
+ // We have to throw here. If this is called, it's because the user asked for "https" binding but for some
347
+ // reason didn't provide a certificate and didn't use the "DefaultHttpsProvider". This means if we no-op,
348
+ // we'll silently downgrade to HTTP, which is bad.
349
+ throw new InvalidOperationException ( CoreStrings . UnableToConfigureHttpsBindings ) ;
350
+ }
351
+ }
308
352
}
309
353
}
0 commit comments