Add better error message when someone does single file publish in ANCM (#10871)

This commit is contained in:
Justin Kotalik 2019-06-05 20:10:43 -07:00 committed by GitHub
parent 2b2bf067dd
commit f080c89e89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 58 additions and 138 deletions

View File

@ -64,6 +64,7 @@ HandlerResolver::LoadRequestHandlerAssembly(const IHttpApplication &pApplication
pConfiguration.QueryProcessPath(),
pApplication.GetApplicationPhysicalPath(),
pConfiguration.QueryArguments(),
errorContext,
options));
location = options->GetDotnetExeLocation();

View File

@ -196,6 +196,7 @@
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ErrorContext.h" />
<ClInclude Include="PollingAppOfflineApplication.h" />
<ClInclude Include="application.h" />
<ClInclude Include="BindingInformation.h" />

View File

@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
struct ErrorContext
{
// TODO consider adding HRESULT here
std::string detailedErrorContent;
USHORT statusCode;
USHORT subStatusCode;
std::string generalErrorType;
std::string errorReason;
};

View File

@ -28,16 +28,6 @@ typedef int(*hostfxr_set_runtime_property_value_fn)(void* host_context_handle, P
typedef int(*hostfxr_run_app_fn)(void* host_context_handle);
typedef int(*hostfxr_close_fn)(void* hostfxr_context_handle);
struct ErrorContext
{
// TODO consider adding HRESULT here
std::string detailedErrorContent;
USHORT statusCode;
USHORT subStatusCode;
std::string generalErrorType;
std::string errorReason;
};
class HostFxrErrorRedirector: NonCopyable
{
public:

View File

@ -23,6 +23,7 @@ HRESULT HostFxrResolutionResult::Create(
_In_ const std::wstring& pcwzProcessPath,
_In_ const std::wstring& pcwzApplicationPhysicalPath,
_In_ const std::wstring& pcwzArguments,
_In_ ErrorContext& errorContext,
_Out_ std::unique_ptr<HostFxrResolutionResult>& ppWrapper)
{
std::filesystem::path knownDotnetLocation;
@ -42,7 +43,8 @@ HRESULT HostFxrResolutionResult::Create(
pcwzArguments,
hostFxrDllPath,
knownDotnetLocation,
arguments);
arguments,
errorContext);
LOG_INFOF(L"Parsed hostfxr options: dotnet location: '%ls' hostfxr path: '%ls' arguments:", knownDotnetLocation.c_str(), hostFxrDllPath.c_str());
for (size_t i = 0; i < arguments.size(); i++)

View File

@ -10,6 +10,8 @@
#include <vector>
#include <string>
#include "ErrorContext.h"
class HostFxrResolutionResult
{
public:
@ -44,6 +46,7 @@ public:
_In_ const std::wstring& pcwzProcessPath,
_In_ const std::wstring& pcwzApplicationPhysicalPath,
_In_ const std::wstring& pcwzArguments,
_In_ ErrorContext& errorContext,
_Out_ std::unique_ptr<HostFxrResolutionResult>& ppWrapper);
private:

View File

@ -21,7 +21,8 @@ HostFxrResolver::GetHostFxrParameters(
const std::wstring &applicationArguments,
fs::path &hostFxrDllPath,
fs::path &dotnetExePath,
std::vector<std::wstring> &arguments
std::vector<std::wstring> &arguments,
ErrorContext& errorContext
)
{
LOG_INFOF(L"Resolving hostfxr parameters for application: '%ls' arguments: '%ls' path: '%ls'",
@ -93,7 +94,13 @@ HostFxrResolver::GetHostFxrParameters(
LOG_INFOF(L"Checking application.dll at '%ls'", applicationDllPath.c_str());
if (!is_regular_file(applicationDllPath))
{
throw InvalidOperationException(format(L"Application .dll was not found at %s", applicationDllPath.c_str()));
errorContext.subStatusCode = 38;
errorContext.errorReason = "Application DLL not found. Confirm the application dll is present. Single-file deployments are not supported in IIS.";
errorContext.generalErrorType = "ANCM Application DLL Not Found";
errorContext.detailedErrorContent = format("Application DLL was not found at %s.", to_multi_byte_string(applicationDllPath, CP_UTF8).c_str());
throw InvalidOperationException(
format(L"Application DLL was not found at %s. Confirm the application dll is present. Single-file deployments are not supported in IIS.",
applicationDllPath.c_str()));
}
hostFxrDllPath = executablePath.parent_path() / "hostfxr.dll";

View File

@ -9,6 +9,8 @@
#include <optional>
#include <string>
#include "ErrorContext.h"
#define READ_BUFFER_SIZE 4096
class HostFxrResolver
@ -23,7 +25,8 @@ public:
const std::wstring &applicationArguments,
std::filesystem::path &hostFxrDllPath,
std::filesystem::path &dotnetExePath,
std::vector<std::wstring> &arguments
std::vector<std::wstring> &arguments,
ErrorContext& errorContext
);
static

View File

@ -43,7 +43,6 @@
<ClCompile Include="ConfigUtilityTests.cpp" />
<ClCompile Include="GlobalVersionTests.cpp" />
<ClCompile Include="Helpers.cpp" />
<ClCompile Include="hostfxr_utility_tests.cpp" />
<ClCompile Include="inprocess_application_tests.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="StandardOutputRedirectionTest.cpp" />
@ -187,4 +186,4 @@
<PropertyGroup>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
</Project>
</Project>

View File

@ -1,122 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#include "stdafx.h"
#include <filesystem>
#include <vector>
#include <string>
#include "HostFxrResolver.h"
#include "Environment.h"
TEST(ParseHostFxrArguments, BasicHostFxrArguments)
{
std::vector<std::wstring> bstrArray;
HostFxrResolver::AppendArguments(
L"exec \"test.dll\"", // args
L"invalid", // physical path to application
bstrArray); // args array.
EXPECT_EQ(2, bstrArray.size());
ASSERT_STREQ(L"exec", bstrArray[0].c_str());
ASSERT_STREQ(L"test.dll", bstrArray[1].c_str());
}
TEST(ParseHostFxrArguments, NoExecProvided)
{
std::vector<std::wstring> bstrArray;
HostFxrResolver::AppendArguments(
L"test.dll", // args
L"ignored", // physical path to application
bstrArray); // args array.
EXPECT_EQ(1, bstrArray.size());
ASSERT_STREQ(L"test.dll", bstrArray[0].c_str());
}
TEST(ParseHostFxrArguments, ConvertDllToAbsolutePath)
{
std::vector<std::wstring> bstrArray;
// we need to use existing dll so let's use ntdll that we know exists everywhere
auto system32 = Environment::ExpandEnvironmentVariables(L"%WINDIR%\\System32");
HostFxrResolver::AppendArguments(
L"exec \"ntdll.dll\"", // args
system32, // physical path to application
bstrArray, // args array.
true); // expandDllPaths
EXPECT_EQ(2, bstrArray.size());
ASSERT_STREQ(L"exec", bstrArray[0].c_str());
ASSERT_STREQ((system32 + L"\\ntdll.dll").c_str(), bstrArray[1].c_str());
}
TEST(ParseHostFxrArguments, ProvideNoArgs_InvalidArgs)
{
std::vector<std::wstring> bstrArray;
std::filesystem::path struHostFxrDllLocation;
std::filesystem::path struExeLocation;
EXPECT_THROW(HostFxrResolver::GetHostFxrParameters(
L"dotnet", // processPath
L"some\\path", // application physical path, ignored.
L"", //arguments
struHostFxrDllLocation,
struExeLocation,
bstrArray), // args array.
InvalidOperationException);
}
TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
{
STRU struAbsolutePathToDotnet;
BOOL fDotnetInProgramFiles;
BOOL is64Bit;
BOOL fIsWow64 = FALSE;
SYSTEM_INFO systemInfo;
IsWow64Process(GetCurrentProcess(), &fIsWow64);
if (fIsWow64)
{
is64Bit = FALSE;
}
else
{
GetNativeSystemInfo(&systemInfo);
is64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
}
if (is64Bit)
{
fDotnetInProgramFiles = std::filesystem::is_regular_file(L"C:/Program Files/dotnet/dotnet.exe");
}
else
{
fDotnetInProgramFiles = std::filesystem::is_regular_file(L"C:/Program Files (x86)/dotnet/dotnet.exe");
}
auto dotnetPath = HostFxrResolver::GetAbsolutePathToDotnetFromProgramFiles();
if (fDotnetInProgramFiles)
{
EXPECT_TRUE(dotnetPath.has_value());
}
else
{
EXPECT_FALSE(dotnetPath.has_value());
}
}
TEST(GetHostFxrArguments, InvalidParams)
{
std::vector<std::wstring> bstrArray;
std::filesystem::path struHostFxrDllLocation;
std::filesystem::path struExeLocation;
EXPECT_THROW(HostFxrResolver::GetHostFxrParameters(
L"bogus", // processPath
L"", // application physical path, ignored.
L"ignored", //arguments
struHostFxrDllLocation,
struExeLocation,
bstrArray), // args array.
InvalidOperationException);
}

View File

@ -195,6 +195,8 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
auto context = std::make_shared<ExecuteClrContext>();
ErrorContext errorContext; // unused
if (s_fMainCallback == nullptr)
{
THROW_IF_FAILED(HostFxrResolutionResult::Create(
@ -202,6 +204,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
m_pConfig->QueryProcessPath(),
QueryApplicationPhysicalPath(),
m_pConfig->QueryArguments(),
errorContext,
hostFxrResolutionResult
));

View File

@ -365,6 +365,24 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess
EventLogHelpers.VerifyEventLogEvent(deploymentResult, EventLogHelpers.InProcessFailedToFindNativeDependencies(deploymentResult), Logger);
}
[ConditionalFact]
public async Task SingleExecutable_FailedToFindNativeDependencies()
{
var deploymentParameters = Fixture.GetBaseDeploymentParameters(Fixture.InProcessTestSite);
deploymentParameters.ApplicationType = ApplicationType.Standalone;
var deploymentResult = await DeployAsync(deploymentParameters);
File.Delete(Path.Combine(deploymentResult.ContentRoot, "InProcessWebSite.dll"));
if (DeployerSelector.HasNewShim)
{
await AssertSiteFailsToStartWithInProcessStaticContent(deploymentResult, "HTTP Error 500.38 - ANCM Application DLL Not Found");
}
else
{
await AssertSiteFailsToStartWithInProcessStaticContent(deploymentResult);
}
}
[ConditionalFact]
public async Task TargedDifferenceSharedFramework_FailedToFindNativeDependenciesErrorInResponse()
{

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Features;