Skip to content

Commit

Permalink
chore: Add fail-fast logics when --serve option enabled & port is a…
Browse files Browse the repository at this point in the history
…lready used (#9690)

chore: Add fail-fast logics when port is already used on serve
  • Loading branch information
filzrev committed Feb 18, 2024
1 parent b05d8f1 commit c7346f1
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/docfx/Models/BuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ public override int Execute(CommandContext context, BuildCommandOptions settings
{
return CommandHelper.Run(settings, () =>
{
if (settings.Serve && CommandHelper.IsTcpPortAlreadyUsed(settings.Host, settings.Port))
{
Logger.LogError($"Serve option specified. But TCP port {settings.Port ?? 8080} is already being in use.");
return;
}
var (config, baseDirectory) = Docset.GetConfig(settings.ConfigFile);
MergeOptionsToConfig(settings, config.build, baseDirectory);
var serveDirectory = RunBuild.Exec(config.build, new(), baseDirectory, settings.OutputFolder);
Expand Down
33 changes: 33 additions & 0 deletions src/docfx/Models/CommandHelper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net;
using System.Net.NetworkInformation;
using Docfx.Common;

#nullable enable

namespace Docfx;

internal class CommandHelper
Expand Down Expand Up @@ -49,4 +53,33 @@ public static int Run(LogOptions options, Action run)

return Logger.HasError ? -1 : 0;
}

public static bool IsTcpPortAlreadyUsed(string? host, int? port)
{
port ??= 8080;

var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
var ipEndpoints = ipGlobalProperties.GetActiveTcpListeners()
.Where(x => x.Port == port);

if (!ipEndpoints.Any())
return false; // Specified port is not used by any endpoint.

switch (host)
{
case null:
case "localhost":
return ipEndpoints.Any(x => IPAddress.IsLoopback(x.Address)); // Check both IPv4/IPv6 loopback address.
default:
if (IPAddress.TryParse(host, out var address))
{
return ipEndpoints.Any(x => x.Address == address);
}
else
{
// Anything not recognized as a valid IP address (e.g. `*`) binds to all IPv4 and IPv6 IPAddresses.
return true;
}
}
}
}
7 changes: 7 additions & 0 deletions src/docfx/Models/DefaultCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.ComponentModel;
using System.Reflection;
using Docfx.Common;
using Docfx.Dotnet;
using Docfx.Pdf;
using Spectre.Console.Cli;
Expand All @@ -29,6 +30,12 @@ public override int Execute(CommandContext context, Options options)

return CommandHelper.Run(options, () =>
{
if (options.Serve && CommandHelper.IsTcpPortAlreadyUsed(options.Host, options.Port))
{
Logger.LogError($"Serve option specified. But TCP port {options.Port ?? 8080} is already being in use.");
return;
}
var (config, configDirectory) = Docset.GetConfig(options.ConfigFile);
var outputFolder = options.OutputFolder;
string serveDirectory = null;
Expand Down

0 comments on commit c7346f1

Please sign in to comment.