Skip to content

Commit 2a55105

Browse files
committed
Get the backbone working
1 parent 4825772 commit 2a55105

File tree

9 files changed

+105
-29
lines changed

9 files changed

+105
-29
lines changed

src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ServerErrorHandler.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
#pragma once
5-
#include <utility>
65
#include "requesthandler.h"
76
#include "file_utility.h"
87
#include "Environment.h"

src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/inprocess_application_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ using ::testing::NiceMock;
1313
// Externals defined in inprocess
1414
BOOL g_fProcessDetach;
1515
HANDLE g_hEventLog;
16+
std::wstring g_exceptionEventLog;
1617

1718
namespace InprocessTests
1819
{

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ HANDLE g_hEventLog = NULL;
3030
bool g_fInProcessApplicationCreated = false;
3131
BYTE* g_errorPageContent;
3232
int g_errorPageLength;
33+
std::wstring g_exceptionEventLog;
3334
HINSTANCE g_hServerModule;
3435

3536
HRESULT

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "HostFxr.h"
1616

1717
IN_PROCESS_APPLICATION* IN_PROCESS_APPLICATION::s_Application = NULL;
18+
extern std::wstring g_exceptionEventLog;
1819

1920
IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION(
2021
IHttpServer& pHttpServer,
@@ -235,8 +236,15 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
235236
LOG_INFOF(L"Setting current directory to %s", this->QueryApplicationPhysicalPath().c_str());
236237
}
237238

238-
bool clrThreadExited;
239+
// TODO this doesn't work when running in VS and with the shared framework.
240+
// We could hack it so we check if the dll exists before and after setting the current directory
241+
// But the best solution would be to be able to set a specific assembly name rather than full path
242+
// See: https://github.com/dotnet/core-setup/issues/5556
243+
auto startupHookDll = Environment::GetCurrentDirectoryValue() + std::wstring(L"\\Microsoft.AspNetCore.Server.IIS.dll");
244+
245+
SetEnvironmentVariable(L"DOTNET_STARTUP_HOOKS", startupHookDll.c_str());
239246

247+
bool clrThreadExited;
240248
{
241249
auto redirectionOutput = LoggingHelpers::CreateOutputs(
242250
m_pConfig->QueryStdoutLogEnabled(),
@@ -460,10 +468,7 @@ IN_PROCESS_APPLICATION::SetEnvironmentVariablesOnWorkerProcess()
460468
{
461469
LOG_INFOF(L"Setting environment variable %ls=%ls", variable.first.c_str(), variable.second.c_str());
462470
SetEnvironmentVariable(variable.first.c_str(), variable.second.c_str());
463-
}
464-
465-
auto startupHookDll = Environment::GetCurrentDirectoryValue() + std::wstring(L"\\Microsoft.AspNetCore.Server.IIS.dll");
466-
SetEnvironmentVariable(L"DOTNET_STARTUP_HOOKS", startupHookDll.c_str());
471+
}
467472

468473
return S_OK;
469474
}
@@ -475,6 +480,14 @@ IN_PROCESS_APPLICATION::UnexpectedThreadExit(const ExecuteClrContext& context) c
475480

476481
if (context.m_exceptionCode != 0)
477482
{
483+
if (!g_exceptionEventLog.empty())
484+
{
485+
EventLog::Error(
486+
ASPNETCORE_EVENT_GENERAL_ERROR,
487+
g_exceptionEventLog.c_str()
488+
);
489+
}
490+
478491
if (!content.empty())
479492
{
480493
EventLog::Error(

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
#include "inprocessapplication.h"
55
#include "inprocesshandler.h"
66
#include "requesthandler_config.h"
7+
#include "EventLog.h"
78

89
extern bool g_fInProcessApplicationCreated;
910
extern BYTE* g_errorPageContent;
1011
extern int g_errorPageLength;
12+
extern IHttpServer* g_pHttpServer;
13+
extern std::wstring g_exceptionEventLog;
1114

1215
//
1316
// Initialization export
@@ -238,7 +241,7 @@ http_read_request_bytes(
238241
{
239242
return E_FAIL;
240243
}
241-
IHttpRequest *pHttpRequest = (IHttpRequest*)pInProcessHandler->QueryHttpContext()->GetRequest();
244+
IHttpRequest* pHttpRequest = (IHttpRequest*)pInProcessHandler->QueryHttpContext()->GetRequest();
242245

243246
// Check if there is anything to read
244247
if (pHttpRequest->GetRemainingEntityBytes() > 0)
@@ -269,7 +272,7 @@ http_write_response_bytes(
269272
_In_ BOOL* pfCompletionExpected
270273
)
271274
{
272-
IHttpResponse *pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
275+
IHttpResponse* pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
273276
BOOL fAsync = TRUE;
274277
BOOL fMoreData = TRUE;
275278
DWORD dwBytesSent = 0;
@@ -293,7 +296,7 @@ http_flush_response_bytes(
293296
_Out_ BOOL* pfCompletionExpected
294297
)
295298
{
296-
IHttpResponse *pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
299+
IHttpResponse* pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
297300

298301
BOOL fAsync = TRUE;
299302
DWORD dwBytesSent = 0;
@@ -318,7 +321,7 @@ http_websockets_read_bytes(
318321
_In_ BOOL* pfCompletionPending
319322
)
320323
{
321-
IHttpRequest3 *pHttpRequest = (IHttpRequest3*)pInProcessHandler->QueryHttpContext()->GetRequest();
324+
IHttpRequest3* pHttpRequest = (IHttpRequest3*)pInProcessHandler->QueryHttpContext()->GetRequest();
322325

323326
BOOL fAsync = TRUE;
324327

@@ -345,7 +348,7 @@ http_websockets_write_bytes(
345348
_In_ BOOL* pfCompletionExpected
346349
)
347350
{
348-
IHttpResponse2 *pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
351+
IHttpResponse2* pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
349352

350353
BOOL fAsync = TRUE;
351354
BOOL fMoreData = TRUE;
@@ -373,7 +376,7 @@ http_websockets_flush_bytes(
373376
_In_ BOOL* pfCompletionExpected
374377
)
375378
{
376-
IHttpResponse2 *pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
379+
IHttpResponse2* pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
377380

378381
BOOL fAsync = TRUE;
379382
BOOL fMoreData = TRUE;
@@ -510,9 +513,21 @@ EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
510513
VOID
511514
http_set_startup_error_page_content(_In_ byte* errorPageContent, int length)
512515
{
513-
g_errorPageContent = new BYTE[length];
514-
g_errorPageLength = length;
515-
memcpy(g_errorPageContent, errorPageContent, length);
516+
// Only set the response if we are running under IISExpress for security reasons.
517+
if (g_pHttpServer->IsCommandLineLaunch())
518+
{
519+
g_errorPageContent = new BYTE[length];
520+
g_errorPageLength = length;
521+
memcpy(g_errorPageContent, errorPageContent, length);
522+
}
523+
}
524+
525+
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
526+
VOID
527+
http_set_startup_error_event_log(_In_ LPCWSTR errorPageContent)
528+
{
529+
g_exceptionEventLog = std::wstring(errorPageContent);
516530
}
531+
// TODO log exception message here.
517532

518533
// End of export

src/Servers/IIS/IIS/samples/NativeIISSample/Startup.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,14 @@ public void Configure(IApplicationBuilder app)
118118

119119
public static void Main(string[] args)
120120
{
121-
throw new InvalidOperationException("ex!");
122-
123-
//var host = new WebHostBuilder()
124-
// .UseKestrel()
125-
// .UseIIS()
126-
// .UseIISIntegration()
127-
// .UseStartup<Startup>()
128-
// .Build();
129-
130-
//host.Run();
121+
var host = new WebHostBuilder()
122+
.UseKestrel()
123+
.UseIIS()
124+
.UseIISIntegration()
125+
.UseStartup<Startup>()
126+
.Build();
127+
128+
host.Run();
131129
}
132130
}
133131
}

src/Servers/IIS/IIS/src/NativeMethods.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace Microsoft.AspNetCore.Server.IIS
1010
{
11-
public static class NativeMethods
11+
internal static class NativeMethods
1212
{
1313
internal const int HR_OK = 0;
1414
internal const int ERROR_NOT_FOUND = unchecked((int)0x80070490);
@@ -150,6 +150,9 @@ private static extern unsafe int http_websockets_write_bytes(
150150
[DllImport(AspNetCoreModuleDll)]
151151
private static extern unsafe int http_set_startup_error_page_content(byte* content, int contentLength);
152152

153+
[DllImport(AspNetCoreModuleDll)]
154+
private static extern void http_set_startup_error_event_log([MarshalAs(UnmanagedType.LPWStr)]string content);
155+
153156
public static void HttpPostCompletion(IntPtr pInProcessHandler, int cbBytes)
154157
{
155158
Validate(http_post_completion(pInProcessHandler, cbBytes));
@@ -308,6 +311,11 @@ internal static unsafe void HttpSetStartupErrorPageContent(byte[] content)
308311
}
309312
}
310313

314+
internal static void HttpSetStartupExceptionEventLogMessage(string content)
315+
{
316+
http_set_startup_error_event_log(content);
317+
}
318+
311319
private static void Validate(int hr)
312320
{
313321
if (hr != HR_OK)

src/Servers/IIS/IIS/src/StartupHook.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Reflection;
99
using System.Runtime.InteropServices;
1010
using System.Security.Claims;
11+
using System.Text;
1112
using System.Threading;
1213
using System.Threading.Tasks;
1314
using Microsoft.AspNetCore.Hosting.Views;
@@ -19,20 +20,26 @@
1920

2021
internal class StartupHook
2122
{
23+
/// <summary>
24+
/// Startup hooks are pieces of code that will run before a users program main executes
25+
/// See: https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/host-startup-hook.md
26+
/// </summary>
2227
public static void Initialize()
2328
{
2429
// TODO make this unhandled exception
2530
AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
2631
{
2732
var exception = eventArgs.Exception;
2833

34+
// Get the content root from IIS.
2935
var iisConfigData = NativeMethods.HttpGetApplicationProperties();
3036
var contentRoot = iisConfigData.pwzFullApplicationPath.TrimEnd(Path.DirectorySeparatorChar);
3137

3238
var model = new ErrorPageModel
3339
{
3440
RuntimeDisplayName = RuntimeInformation.FrameworkDescription
3541
};
42+
3643
var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).GetTypeInfo().Assembly;
3744
var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString();
3845
var clrVersion = assemblyVersion;
@@ -51,12 +58,28 @@ public static void Initialize()
5158
model.ErrorDetails = exceptionDetailProvider.GetDetails(exception);
5259

5360
var errorPage = new ErrorPage(model);
61+
62+
// Create a temporary HttpContext to write the response into.
5463
var context = new IntermediateHttpContext();
64+
// Sync over async here, but you can't have async code in startup hooks.
5565
errorPage.ExecuteAsync(context).GetAwaiter().GetResult();
56-
context.Response.Body.Position = 0;
57-
var content = ((MemoryStream)context.Response.Body).ToArray();
66+
67+
// Get the raw content and set the error page.
68+
var stream = (MemoryStream)context.Response.Body;
69+
stream.Position = 0;
70+
var content = stream.ToArray();
5871

5972
NativeMethods.HttpSetStartupErrorPageContent(content);
73+
74+
// Second part of startup hook is sending to event log.
75+
var stringBuilder = new StringBuilder();
76+
stringBuilder.Append("Application: " + Environment.NewLine); // TODO dll name
77+
stringBuilder.Append("CoreCLR Version: " + clrVersion + Environment.NewLine);
78+
stringBuilder.Append("Description: The process was terminated due to an unhandled exception." + Environment.NewLine);
79+
stringBuilder.Append($"Exception Info: {exception.GetType().ToString()}: {exception.Message}" + Environment.NewLine);
80+
stringBuilder.Append(exception.StackTrace);
81+
82+
NativeMethods.HttpSetStartupExceptionEventLogMessage(stringBuilder.ToString());
6083
};
6184
}
6285

@@ -68,7 +91,7 @@ public IntermediateHttpContext()
6891

6992
public override IFeatureCollection Features => throw new NotImplementedException();
7093

71-
public override HttpRequest Request => null;
94+
public override HttpRequest Request => null;
7295

7396
public override HttpResponse Response { get; } = new IntermediateResponse();
7497

src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,24 @@ public async Task StartupIsSuspendedWhenEventIsUsed()
560560
await request;
561561
}
562562

563+
[ConditionalFact]
564+
public async Task ExceptionIsLoggedToEventLogAndPutInResponseForIISExpress()
565+
{
566+
var deploymentParameters = _fixture.GetBaseDeploymentParameters();
567+
deploymentParameters.TransformArguments((a, _) => $"{a} Throw");
568+
569+
var deploymentResult = await DeployAsync(deploymentParameters);
570+
var result = await deploymentResult.HttpClient.GetAsync("/");
571+
Assert.False(result.IsSuccessStatusCode);
572+
573+
if (deploymentParameters.ServerType == ServerType.IISExpress)
574+
{
575+
var content = await result.Content.ReadAsStringAsync();
576+
Assert.Contains("InvalidOperationException", content);
577+
Assert.Contains("TestSite.Program.Main(string[] args)", content);
578+
}
579+
}
580+
563581
private static void MoveApplication(
564582
IISDeploymentParameters parameters,
565583
string subdirectory)

0 commit comments

Comments
 (0)