Skip to content

Commit 6fa88b1

Browse files
authored
HTTP/HTTPS in Swagger UI + oauth2+redirect.html (#253)
1 parent 7d1b47f commit 6fa88b1

15 files changed

Lines changed: 555 additions & 21 deletions

File tree

src/Microsoft.Azure.Functions.Worker.Extensions.OpenApi/Document.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Newtonsoft.Json.Serialization;
1515

1616
using GenericExtensions = Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions.GenericExtensions;
17+
using HttpRequestDataObjectExtensions = Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions.HttpRequestDataObjectExtensions;
1718
using OpenApiDocumentExtensions = Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions.OpenApiDocumentExtensions;
1819
using StringExtensions = Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions.StringExtensions;
1920

@@ -73,7 +74,7 @@ public IDocument AddServer(IHttpRequestDataObject req, string routePrefix, IOpen
7374
this._req = req;
7475

7576
var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
76-
var baseUrl = $"{this._req.Scheme}://{this._req.Host}{prefix}";
77+
var baseUrl = $"{HttpRequestDataObjectExtensions.GetScheme(this._req, options)}://{this._req.Host}{prefix}";
7778

7879
var server = new OpenApiServer { Url = baseUrl };
7980

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<PropertyGroup>
66
<OutputType>Exe</OutputType>
7-
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
7+
<TargetFramework>netcoreapp3.1</TargetFramework>
88
<AssemblyName>azfuncopenapi</AssemblyName>
99
<RootNamespace>Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI</RootNamespace>
1010
</PropertyGroup>

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Abstractions/IOpenApiConfigurationOptions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,15 @@ public interface IOpenApiConfigurationOptions
2929
/// Gets or sets the value indicating whether to include the requesting hostname or not.
3030
/// </summary>
3131
bool IncludeRequestingHostName { get; set; }
32+
33+
/// <summary>
34+
/// Gets or sets the value indicating whether to force the HTTP protocol or not.
35+
/// </summary>
36+
bool ForceHttp { get; set; }
37+
38+
/// <summary>
39+
/// Gets or sets the value indicating whether to force the HTTPS protocol or not.
40+
/// </summary>
41+
bool ForceHttps { get; set; }
3242
}
3343
}

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Configurations/DefaultOpenApiConfigurationOptions.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public class DefaultOpenApiConfigurationOptions : IOpenApiConfigurationOptions
2020
private const string OpenApiHostNamesKey = "OpenApi__HostNames";
2121
private const string OpenApiVersionKey = "OpenApi__Version";
2222
private const string FunctionsRuntimeEnvironmentKey = "AZURE_FUNCTIONS_ENVIRONMENT";
23+
private const string ForceHttpKey = "OpenApi__ForceHttp";
24+
private const string ForceHttpsKey = "OpenApi__ForceHttps";
2325

2426
/// <inheritdoc />
2527
public virtual OpenApiInfo Info { get; set; } = new OpenApiInfo()
@@ -37,6 +39,12 @@ public class DefaultOpenApiConfigurationOptions : IOpenApiConfigurationOptions
3739
/// <inheritdoc />
3840
public virtual bool IncludeRequestingHostName { get; set; } = IsFunctionsRuntimeEnvironmentDevelopment();
3941

42+
/// <inheritdoc />
43+
public bool ForceHttp { get; set; } = IsHttpForced();
44+
45+
/// <inheritdoc />
46+
public bool ForceHttps { get; set; } = IsHttpsForced();
47+
4048
/// <summary>
4149
/// Gets the OpenAPI document version.
4250
/// </summary>
@@ -105,6 +113,28 @@ protected static bool IsFunctionsRuntimeEnvironmentDevelopment()
105113
return development;
106114
}
107115

116+
/// <summary>
117+
/// Checks whether HTTP is forced or not.
118+
/// </summary>
119+
/// <returns>Returns <c>True</c>, if HTTP is forced; otherwise returns <c>False</c>.</returns>
120+
protected static bool IsHttpForced()
121+
{
122+
var development = bool.TryParse(Environment.GetEnvironmentVariable(ForceHttpKey), out var result) ? result : false;;
123+
124+
return development;
125+
}
126+
127+
/// <summary>
128+
/// Checks whether HTTPS is forced or not.
129+
/// </summary>
130+
/// <returns>Returns <c>True</c>, if HTTPS is forced; otherwise returns <c>False</c>.</returns>
131+
protected static bool IsHttpsForced()
132+
{
133+
var development = bool.TryParse(Environment.GetEnvironmentVariable(ForceHttpsKey), out var result) ? result : false;;
134+
135+
return development;
136+
}
137+
108138
private static OpenApiVersionType DefaultOpenApiVersion()
109139
{
110140
return OpenApiVersionType.V2;

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Configurations/OpenApiSettings.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,11 @@ public sealed class OpenApiSettings : IOpenApiConfigurationOptions
2828

2929
/// <inheritdoc />
3030
public bool IncludeRequestingHostName { get; set; }
31+
32+
/// <inheritdoc />
33+
public bool ForceHttp { get; set; }
34+
35+
/// <inheritdoc />
36+
public bool ForceHttps { get; set; }
3137
}
3238
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
2+
3+
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions
4+
{
5+
/// <summary>
6+
/// This represents the extension entity for <see cref="IHttpRequestDataObject"/>.
7+
/// </summary>
8+
public static class HttpRequestDataObjectExtensions
9+
{
10+
private const string HTTP = "http";
11+
private const string HTTPS = "https";
12+
13+
/// <summary>
14+
/// Gets the scheme whether to return either HTTP or HTTPS depending on the condition.
15+
/// /// </summary>
16+
/// <param name="req"><see cref="IHttpRequestDataObject"/> instance.</param>
17+
/// <param name="options"><see cref="IOpenApiConfigurationOptions"/> instance.</param>
18+
/// <returns>Return either "HTTP" or "HTTPS", depending on the condition.</returns>
19+
public static string GetScheme(this IHttpRequestDataObject req, IOpenApiConfigurationOptions options)
20+
{
21+
req.ThrowIfNullOrDefault();
22+
23+
if (options.IsNullOrDefault())
24+
{
25+
return req.Scheme;
26+
}
27+
28+
if (options.ForceHttps)
29+
{
30+
return HTTPS;
31+
}
32+
33+
if (options.ForceHttp)
34+
{
35+
return HTTP;
36+
}
37+
38+
return req.Scheme;
39+
}
40+
}
41+
}

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/SwaggerUI.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.IO;
43
using System.Linq;
54
using System.Reflection;
@@ -59,12 +58,16 @@ public ISwaggerUI AddServer(IHttpRequestDataObject req, string routePrefix, IOpe
5958
this._req = req;
6059

6160
var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
62-
var baseUrl = $"{this._req.Scheme}://{this._req.Host}{prefix}";
61+
var baseUrl = $"{this._req.GetScheme(options)}://{this._req.Host}{prefix}";
62+
var absolutePath = default(string);
6363

6464
if (options.IsNullOrDefault())
6565
{
6666
this._baseUrl = baseUrl;
6767

68+
absolutePath = new Uri(this._baseUrl).AbsolutePath.Trim('/');
69+
this._swaggerUiApiPrefix = absolutePath;
70+
6871
return this;
6972
}
7073

@@ -85,7 +88,9 @@ public ISwaggerUI AddServer(IHttpRequestDataObject req, string routePrefix, IOpe
8588
}
8689

8790
this._baseUrl = servers.First().Url;
88-
this._swaggerUiApiPrefix = prefix;
91+
92+
absolutePath = new Uri(this._baseUrl).AbsolutePath.Trim('/');
93+
this._swaggerUiApiPrefix = absolutePath;
8994

9095
return this;
9196
}

src/Microsoft.Azure.WebJobs.Extensions.OpenApi/Document.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public IDocument AddServer(IHttpRequestDataObject req, string routePrefix, IOpen
6969
this._req = req;
7070

7171
var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
72-
var baseUrl = $"{this._req.Scheme}://{this._req.Host}{prefix}";
72+
var baseUrl = $"{this._req.GetScheme(options)}://{this._req.Host}{prefix}";
7373

7474
var server = new OpenApiServer { Url = baseUrl };
7575

test/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Tests/DocumentTests.cs

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public void Given_Info_When_AddMetadata_Invoked_Then_It_Should_Return_Result(str
7272
[DataRow("https", "localhost", 47071, null)]
7373
[DataRow("https", "localhost", 47071, "")]
7474
[DataRow("https", "localhost", 47071, "api")]
75-
public void Given_NoOptions_When_AddMetadata_Invoked_Then_It_Should_Return_Result(string scheme, string host, int port, string routePrefix)
75+
public void Given_NoOptions_When_AddServer_Invoked_Then_It_Should_Return_Result(string scheme, string host, int port, string routePrefix)
7676
{
7777
var helper = new Mock<IDocumentHelper>();
7878
var doc = new Document(helper.Object);
@@ -104,7 +104,7 @@ public void Given_NoOptions_When_AddMetadata_Invoked_Then_It_Should_Return_Resul
104104
[DataRow("https", "localhost", 47071, null)]
105105
[DataRow("https", "localhost", 47071, "")]
106106
[DataRow("https", "localhost", 47071, "api")]
107-
public void Given_EmptyOptions_When_AddMetadata_Invoked_Then_It_Should_Return_Result(string scheme, string host, int port, string routePrefix)
107+
public void Given_EmptyOptions_When_AddServer_Invoked_Then_It_Should_Return_Result(string scheme, string host, int port, string routePrefix)
108108
{
109109
var helper = new Mock<IDocumentHelper>();
110110
var doc = new Document(helper.Object);
@@ -139,7 +139,7 @@ public void Given_EmptyOptions_When_AddMetadata_Invoked_Then_It_Should_Return_Re
139139
[DataRow("https", "localhost", 47071, null, "helloworld")]
140140
[DataRow("https", "localhost", 47071, "", "helloworld")]
141141
[DataRow("https", "localhost", 47071, "api", "helloworld")]
142-
public void Given_ExtraServers_And_Include_When_AddMetadata_Invoked_Then_It_Should_Return_Result(string scheme, string host, int port, string routePrefix, string server)
142+
public void Given_ExtraServers_And_Include_When_AddServer_Invoked_Then_It_Should_Return_Result(string scheme, string host, int port, string routePrefix, string server)
143143
{
144144
var helper = new Mock<IDocumentHelper>();
145145
var doc = new Document(helper.Object);
@@ -177,7 +177,7 @@ public void Given_ExtraServers_And_Include_When_AddMetadata_Invoked_Then_It_Shou
177177
[DataRow("https", "localhost", 47071, null, "helloworld")]
178178
[DataRow("https", "localhost", 47071, "", "helloworld")]
179179
[DataRow("https", "localhost", 47071, "api", "helloworld")]
180-
public void Given_ExtraServers_And_Exclude_When_AddMetadata_Invoked_Then_It_Should_Return_Result(string scheme, string host, int port, string routePrefix, string server)
180+
public void Given_ExtraServers_And_Exclude_When_AddServer_Invoked_Then_It_Should_Return_Result(string scheme, string host, int port, string routePrefix, string server)
181181
{
182182
var helper = new Mock<IDocumentHelper>();
183183
var doc = new Document(helper.Object);
@@ -201,6 +201,109 @@ public void Given_ExtraServers_And_Exclude_When_AddMetadata_Invoked_Then_It_Shou
201201
result.OpenApiDocument.Servers.First().Url.Should().Be(server);
202202
}
203203

204+
[DataTestMethod]
205+
[DataRow("http", "localhost", "api", "http://localhost/api")]
206+
[DataRow("https", "localhost", "api", "https://localhost/api")]
207+
[DataRow("http", "localhost:7071", "api", "http://localhost:7071/api")]
208+
[DataRow("https", "localhost:47071", "api", "https://localhost:47071/api")]
209+
[DataRow("http", "localhost", "", "http://localhost")]
210+
[DataRow("https", "localhost", "", "https://localhost")]
211+
[DataRow("http", "localhost:7071", "", "http://localhost:7071")]
212+
[DataRow("https", "localhost:47071", "", "https://localhost:47071")]
213+
[DataRow("http", "localhost", null, "http://localhost")]
214+
[DataRow("https", "localhost", null, "https://localhost")]
215+
[DataRow("http", "localhost:7071", null, "http://localhost:7071")]
216+
[DataRow("https", "localhost:47071", null, "https://localhost:47071")]
217+
public void Given_NullOptions_When_AddServer_Invoked_Then_It_Should_Return_BaseUrl(string scheme, string host, string routePrefix, string expected)
218+
{
219+
var helper = new Mock<IDocumentHelper>();
220+
var doc = new Document(helper.Object);
221+
222+
var req = new Mock<IHttpRequestDataObject>();
223+
req.SetupGet(p => p.Scheme).Returns(scheme);
224+
225+
var hostString = new HostString(host);
226+
req.SetupGet(p => p.Host).Returns(hostString);
227+
228+
var result = doc.InitialiseDocument()
229+
.AddServer(req.Object, routePrefix, null);
230+
231+
result.OpenApiDocument.Servers.Should().HaveCount(1);
232+
result.OpenApiDocument.Servers.First().Url.Should().Be(expected);
233+
}
234+
235+
[DataTestMethod]
236+
[DataRow("http", "localhost", "api", true, true, "https://localhost/api")]
237+
[DataRow("https", "localhost", "api", true, true, "https://localhost/api")]
238+
[DataRow("http", "localhost:7071", "api", true, true, "https://localhost:7071/api")]
239+
[DataRow("https", "localhost:47071", "api", true, true, "https://localhost:47071/api")]
240+
[DataRow("http", "localhost", "", true, true, "https://localhost")]
241+
[DataRow("https", "localhost", "", true, true, "https://localhost")]
242+
[DataRow("http", "localhost:7071", "", true, true, "https://localhost:7071")]
243+
[DataRow("https", "localhost:47071", "", true, true, "https://localhost:47071")]
244+
[DataRow("http", "localhost", null, true, true, "https://localhost")]
245+
[DataRow("https", "localhost", null, true, true, "https://localhost")]
246+
[DataRow("http", "localhost:7071", null, true, true, "https://localhost:7071")]
247+
[DataRow("https", "localhost:47071", null, true, true, "https://localhost:47071")]
248+
[DataRow("http", "localhost", "api", true, false, "https://localhost/api")]
249+
[DataRow("https", "localhost", "api", true, false, "https://localhost/api")]
250+
[DataRow("http", "localhost:7071", "api", true, false, "https://localhost:7071/api")]
251+
[DataRow("https", "localhost:47071", "api", true, false, "https://localhost:47071/api")]
252+
[DataRow("http", "localhost", "", true, false, "https://localhost")]
253+
[DataRow("https", "localhost", "", true, false, "https://localhost")]
254+
[DataRow("http", "localhost:7071", "", true, false, "https://localhost:7071")]
255+
[DataRow("https", "localhost:47071", "", true, false, "https://localhost:47071")]
256+
[DataRow("http", "localhost", null, true, false, "https://localhost")]
257+
[DataRow("https", "localhost", null, true, false, "https://localhost")]
258+
[DataRow("http", "localhost:7071", null, true, false, "https://localhost:7071")]
259+
[DataRow("https", "localhost:47071", null, true, false, "https://localhost:47071")]
260+
[DataRow("http", "localhost", "api", false, true, "http://localhost/api")]
261+
[DataRow("https", "localhost", "api", false, true, "http://localhost/api")]
262+
[DataRow("http", "localhost:7071", "api", false, true, "http://localhost:7071/api")]
263+
[DataRow("https", "localhost:47071", "api", false, true, "http://localhost:47071/api")]
264+
[DataRow("http", "localhost", "", false, true, "http://localhost")]
265+
[DataRow("https", "localhost", "", false, true, "http://localhost")]
266+
[DataRow("http", "localhost:7071", "", false, true, "http://localhost:7071")]
267+
[DataRow("https", "localhost:47071", "", false, true, "http://localhost:47071")]
268+
[DataRow("http", "localhost", null, false, true, "http://localhost")]
269+
[DataRow("https", "localhost", null, false, true, "http://localhost")]
270+
[DataRow("http", "localhost:7071", null, false, true, "http://localhost:7071")]
271+
[DataRow("https", "localhost:47071", null, false, true, "http://localhost:47071")]
272+
[DataRow("http", "localhost", "api", false, false, "http://localhost/api")]
273+
[DataRow("https", "localhost", "api", false, false, "https://localhost/api")]
274+
[DataRow("http", "localhost:7071", "api", false, false, "http://localhost:7071/api")]
275+
[DataRow("https", "localhost:47071", "api", false, false, "https://localhost:47071/api")]
276+
[DataRow("http", "localhost", "", false, false, "http://localhost")]
277+
[DataRow("https", "localhost", "", false, false, "https://localhost")]
278+
[DataRow("http", "localhost:7071", "", false, false, "http://localhost:7071")]
279+
[DataRow("https", "localhost:47071", "", false, false, "https://localhost:47071")]
280+
[DataRow("http", "localhost", null, false, false, "http://localhost")]
281+
[DataRow("https", "localhost", null, false, false, "https://localhost")]
282+
[DataRow("http", "localhost:7071", null, false, false, "http://localhost:7071")]
283+
[DataRow("https", "localhost:47071", null, false, false, "https://localhost:47071")]
284+
public void Given_Options_When_AddServer_Invoked_Then_It_Should_Return_BaseUrl(string scheme, string host, string routePrefix, bool forceHttps, bool forceHttp, string expected)
285+
{
286+
var helper = new Mock<IDocumentHelper>();
287+
var doc = new Document(helper.Object);
288+
289+
var req = new Mock<IHttpRequestDataObject>();
290+
req.SetupGet(p => p.Scheme).Returns(scheme);
291+
292+
var hostString = new HostString(host);
293+
req.SetupGet(p => p.Host).Returns(hostString);
294+
295+
var options = new Mock<IOpenApiConfigurationOptions>();
296+
options.SetupGet(p => p.ForceHttps).Returns(forceHttps);
297+
options.SetupGet(p => p.ForceHttp).Returns(forceHttp);
298+
options.SetupGet(p => p.Servers).Returns(new List<OpenApiServer>());
299+
300+
var result = doc.InitialiseDocument()
301+
.AddServer(req.Object, routePrefix, options.Object);
302+
303+
result.OpenApiDocument.Servers.Should().HaveCount(1);
304+
result.OpenApiDocument.Servers.First().Url.Should().Be(expected);
305+
}
306+
204307
[TestMethod]
205308
public void Given_Null_When_AddNamingStrategy_Invoked_Then_It_Should_Throw_Exception()
206309
{

test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
55

66
<IsPackable>false</IsPackable>
77

0 commit comments

Comments
 (0)