commit
6629236a0e
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>ASP.NET Core components for working with the IIS AspNetCoreModule.</Description>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
[DllImport(AspNetCoreModuleDll)]
|
||||
private static extern int http_get_server_variable(
|
||||
IntPtr pInProcessHandler,
|
||||
[MarshalAs(UnmanagedType.AnsiBStr)] string variableName,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string variableName,
|
||||
[MarshalAs(UnmanagedType.BStr)] out string value);
|
||||
|
||||
[DllImport(AspNetCoreModuleDll)]
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
IHttpRequestFeature,
|
||||
IHttpResponseFeature,
|
||||
IHttpUpgradeFeature,
|
||||
IHttpConnectionFeature,
|
||||
IHttpRequestLifetimeFeature,
|
||||
IHttpRequestIdentifierFeature,
|
||||
IHttpAuthenticationFeature,
|
||||
IServerVariablesFeature
|
||||
{
|
||||
|
|
@ -183,49 +181,12 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
|
||||
bool IHttpResponseFeature.HasStarted => HasResponseStarted;
|
||||
|
||||
// The UpgradeAvailable Feature is set on the first request to the server.
|
||||
bool IHttpUpgradeFeature.IsUpgradableRequest => _websocketAvailability == WebsocketAvailabilityStatus.Available;
|
||||
bool IHttpUpgradeFeature.IsUpgradableRequest => _server.IsWebSocketAvailible(_pInProcessHandler);
|
||||
|
||||
bool IFeatureCollection.IsReadOnly => false;
|
||||
|
||||
int IFeatureCollection.Revision => _featureRevision;
|
||||
|
||||
IPAddress IHttpConnectionFeature.RemoteIpAddress
|
||||
{
|
||||
get => RemoteIpAddress;
|
||||
set => RemoteIpAddress = value;
|
||||
}
|
||||
|
||||
IPAddress IHttpConnectionFeature.LocalIpAddress
|
||||
{
|
||||
get => LocalIpAddress;
|
||||
set => LocalIpAddress = value;
|
||||
}
|
||||
|
||||
int IHttpConnectionFeature.RemotePort
|
||||
{
|
||||
get => RemotePort;
|
||||
set => RemotePort = value;
|
||||
}
|
||||
|
||||
int IHttpConnectionFeature.LocalPort
|
||||
{
|
||||
get => LocalPort;
|
||||
set => LocalPort = value;
|
||||
}
|
||||
|
||||
string IHttpConnectionFeature.ConnectionId
|
||||
{
|
||||
get => RequestConnectionId;
|
||||
set => RequestConnectionId = value;
|
||||
}
|
||||
|
||||
string IHttpRequestIdentifierFeature.TraceIdentifier
|
||||
{
|
||||
get => TraceIdentifier;
|
||||
set => TraceIdentifier = value;
|
||||
}
|
||||
|
||||
ClaimsPrincipal IHttpAuthenticationFeature.User
|
||||
{
|
||||
get => User;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
// 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.
|
||||
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||
{
|
||||
internal partial class IISHttpContext : IHttpConnectionFeature
|
||||
{
|
||||
IPAddress IHttpConnectionFeature.RemoteIpAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RemoteIpAddress == null)
|
||||
{
|
||||
InitializeRemoteEndpoint();
|
||||
}
|
||||
|
||||
return RemoteIpAddress;
|
||||
}
|
||||
set => RemoteIpAddress = value;
|
||||
}
|
||||
|
||||
IPAddress IHttpConnectionFeature.LocalIpAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
if (LocalIpAddress == null)
|
||||
{
|
||||
InitializeLocalEndpoint();
|
||||
}
|
||||
return LocalIpAddress;
|
||||
}
|
||||
set => LocalIpAddress = value;
|
||||
}
|
||||
|
||||
int IHttpConnectionFeature.RemotePort
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RemoteIpAddress == null)
|
||||
{
|
||||
InitializeRemoteEndpoint();
|
||||
}
|
||||
|
||||
return RemotePort;
|
||||
}
|
||||
set => RemotePort = value;
|
||||
}
|
||||
|
||||
int IHttpConnectionFeature.LocalPort
|
||||
{
|
||||
get
|
||||
{
|
||||
if (LocalIpAddress == null)
|
||||
{
|
||||
InitializeLocalEndpoint();
|
||||
}
|
||||
|
||||
return LocalPort;
|
||||
}
|
||||
set => LocalPort = value;
|
||||
}
|
||||
|
||||
string IHttpConnectionFeature.ConnectionId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RequestConnectionId == null)
|
||||
{
|
||||
InitializeConnectionId();
|
||||
}
|
||||
|
||||
return RequestConnectionId;
|
||||
}
|
||||
set => RequestConnectionId = value;
|
||||
}
|
||||
|
||||
private void InitializeLocalEndpoint()
|
||||
{
|
||||
var localEndPoint = GetLocalEndPoint();
|
||||
LocalIpAddress = localEndPoint.GetIPAddress();
|
||||
LocalPort = localEndPoint.GetPort();
|
||||
}
|
||||
|
||||
private void InitializeRemoteEndpoint()
|
||||
{
|
||||
var remoteEndPoint = GetRemoteEndPoint();
|
||||
RemoteIpAddress = remoteEndPoint.GetIPAddress();
|
||||
RemotePort = remoteEndPoint.GetPort();
|
||||
}
|
||||
|
||||
private void InitializeConnectionId()
|
||||
{
|
||||
RequestConnectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||
{
|
||||
internal partial class IISHttpContext : IHttpRequestIdentifierFeature
|
||||
{
|
||||
string IHttpRequestIdentifierFeature.TraceIdentifier
|
||||
{
|
||||
get
|
||||
{
|
||||
if (TraceIdentifier == null)
|
||||
{
|
||||
InitializeHttpRequestIdentifierFeature();
|
||||
}
|
||||
|
||||
return TraceIdentifier;
|
||||
}
|
||||
set => TraceIdentifier = value;
|
||||
}
|
||||
|
||||
private unsafe void InitializeHttpRequestIdentifierFeature()
|
||||
{
|
||||
// Copied from WebListener
|
||||
// This is the base GUID used by HTTP.SYS for generating the activity ID.
|
||||
// HTTP.SYS overwrites the first 8 bytes of the base GUID with RequestId to generate ETW activity ID.
|
||||
// The requestId should be set by the NativeRequestContext
|
||||
var guid = new Guid(0xffcb4c93, 0xa57f, 0x453c, 0xb6, 0x3f, 0x84, 0x71, 0xc, 0x79, 0x67, 0xbb);
|
||||
*((ulong*)&guid) = RequestId;
|
||||
|
||||
// TODO: Also make this not slow
|
||||
TraceIdentifier = guid.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,11 +28,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
private const int PauseWriterThreshold = 65536;
|
||||
private const int ResumeWriterTheshold = PauseWriterThreshold / 2;
|
||||
|
||||
// TODO make this static again.
|
||||
private static WebsocketAvailabilityStatus _websocketAvailability = WebsocketAvailabilityStatus.Uninitialized;
|
||||
|
||||
protected readonly IntPtr _pInProcessHandler;
|
||||
|
||||
private readonly IISOptions _options;
|
||||
|
||||
private bool _reading; // To know whether we are currently in a read operation.
|
||||
private volatile bool _hasResponseStarted;
|
||||
|
||||
|
|
@ -60,116 +60,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
private const string NtlmString = "NTLM";
|
||||
private const string NegotiateString = "Negotiate";
|
||||
private const string BasicString = "Basic";
|
||||
private const string WebSocketVersionString = "WEBSOCKET_VERSION";
|
||||
|
||||
internal unsafe IISHttpContext(MemoryPool<byte> memoryPool, IntPtr pInProcessHandler, IISOptions options, IISHttpServer server)
|
||||
: base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.HttpGetRawRequest(pInProcessHandler))
|
||||
{
|
||||
_thisHandle = GCHandle.Alloc(this);
|
||||
|
||||
_memoryPool = memoryPool;
|
||||
_pInProcessHandler = pInProcessHandler;
|
||||
_options = options;
|
||||
_server = server;
|
||||
|
||||
NativeMethods.HttpSetManagedContext(pInProcessHandler, (IntPtr)_thisHandle);
|
||||
unsafe
|
||||
{
|
||||
Method = GetVerb();
|
||||
|
||||
RawTarget = GetRawUrl();
|
||||
// TODO version is slow.
|
||||
HttpVersion = GetVersion();
|
||||
Scheme = SslStatus != SslStatus.Insecure ? Constants.HttpsScheme : Constants.HttpScheme;
|
||||
KnownMethod = VerbId;
|
||||
|
||||
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(GetRawUrlInBytes());
|
||||
|
||||
if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal))
|
||||
{
|
||||
PathBase = string.Empty;
|
||||
Path = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Path and pathbase are unescaped by RequestUriBuilder
|
||||
// The UsePathBase middleware will modify the pathbase and path correctly
|
||||
PathBase = string.Empty;
|
||||
Path = originalPath;
|
||||
}
|
||||
|
||||
var cookedUrl = GetCookedUrl();
|
||||
QueryString = cookedUrl.GetQueryString() ?? string.Empty;
|
||||
|
||||
// TODO: Avoid using long.ToString, it's pretty slow
|
||||
RequestConnectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
// Copied from WebListener
|
||||
// This is the base GUID used by HTTP.SYS for generating the activity ID.
|
||||
// HTTP.SYS overwrites the first 8 bytes of the base GUID with RequestId to generate ETW activity ID.
|
||||
// The requestId should be set by the NativeRequestContext
|
||||
var guid = new Guid(0xffcb4c93, 0xa57f, 0x453c, 0xb6, 0x3f, 0x84, 0x71, 0xc, 0x79, 0x67, 0xbb);
|
||||
*((ulong*)&guid) = RequestId;
|
||||
|
||||
// TODO: Also make this not slow
|
||||
TraceIdentifier = guid.ToString();
|
||||
|
||||
var localEndPoint = GetLocalEndPoint();
|
||||
LocalIpAddress = localEndPoint.GetIPAddress();
|
||||
LocalPort = localEndPoint.GetPort();
|
||||
|
||||
var remoteEndPoint = GetRemoteEndPoint();
|
||||
RemoteIpAddress = remoteEndPoint.GetIPAddress();
|
||||
RemotePort = remoteEndPoint.GetPort();
|
||||
StatusCode = 200;
|
||||
|
||||
RequestHeaders = new RequestHeaders(this);
|
||||
HttpResponseHeaders = new HeaderCollection();
|
||||
ResponseHeaders = HttpResponseHeaders;
|
||||
|
||||
if (options.ForwardWindowsAuthentication)
|
||||
{
|
||||
WindowsUser = GetWindowsPrincipal();
|
||||
if (options.AutomaticAuthentication)
|
||||
{
|
||||
User = WindowsUser;
|
||||
}
|
||||
}
|
||||
|
||||
ResetFeatureCollection();
|
||||
|
||||
// Check if the Http upgrade feature is available in IIS.
|
||||
// To check this, we can look at the server variable WEBSOCKET_VERSION
|
||||
// And see if there is a version. Same check that Katana did:
|
||||
// https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Host.SystemWeb/OwinAppContext.cs#L125
|
||||
// Actively not locking here as acquiring a lock on every request will hurt perf more than checking the
|
||||
// server variables a few extra times if a bunch of requests hit the server at the same time.
|
||||
if (_websocketAvailability == WebsocketAvailabilityStatus.Uninitialized)
|
||||
{
|
||||
var webSocketsAvailable = NativeMethods.HttpTryGetServerVariable(pInProcessHandler, WebSocketVersionString, out var webSocketsSupported)
|
||||
&& !string.IsNullOrEmpty(webSocketsSupported);
|
||||
|
||||
_websocketAvailability = webSocketsAvailable ?
|
||||
WebsocketAvailabilityStatus.Available :
|
||||
WebsocketAvailabilityStatus.NotAvailable;
|
||||
}
|
||||
|
||||
if (_websocketAvailability == WebsocketAvailabilityStatus.NotAvailable)
|
||||
{
|
||||
_currentIHttpUpgradeFeature = null;
|
||||
}
|
||||
}
|
||||
|
||||
RequestBody = new IISHttpRequestBody(this);
|
||||
ResponseBody = new IISHttpResponseBody(this);
|
||||
|
||||
Input = new Pipe(new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.ThreadPool, minimumSegmentSize: MinAllocBufferSize));
|
||||
var pipe = new Pipe(new PipeOptions(
|
||||
_memoryPool,
|
||||
readerScheduler: PipeScheduler.ThreadPool,
|
||||
pauseWriterThreshold: PauseWriterThreshold,
|
||||
resumeWriterThreshold: ResumeWriterTheshold,
|
||||
minimumSegmentSize: MinAllocBufferSize));
|
||||
Output = new OutputProducer(pipe);
|
||||
}
|
||||
|
||||
public Version HttpVersion { get; set; }
|
||||
|
|
@ -197,7 +95,74 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
public IHeaderDictionary RequestHeaders { get; set; }
|
||||
public IHeaderDictionary ResponseHeaders { get; set; }
|
||||
private HeaderCollection HttpResponseHeaders { get; set; }
|
||||
internal HttpApiTypes.HTTP_VERB KnownMethod { get; }
|
||||
internal HttpApiTypes.HTTP_VERB KnownMethod { get; private set; }
|
||||
|
||||
protected void InitializeContext()
|
||||
{
|
||||
_thisHandle = GCHandle.Alloc(this);
|
||||
|
||||
NativeMethods.HttpSetManagedContext(_pInProcessHandler, (IntPtr)_thisHandle);
|
||||
|
||||
Method = GetVerb();
|
||||
|
||||
RawTarget = GetRawUrl();
|
||||
// TODO version is slow.
|
||||
HttpVersion = GetVersion();
|
||||
Scheme = SslStatus != SslStatus.Insecure ? Constants.HttpsScheme : Constants.HttpScheme;
|
||||
KnownMethod = VerbId;
|
||||
StatusCode = 200;
|
||||
|
||||
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(GetRawUrlInBytes());
|
||||
|
||||
if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal))
|
||||
{
|
||||
PathBase = string.Empty;
|
||||
Path = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Path and pathbase are unescaped by RequestUriBuilder
|
||||
// The UsePathBase middleware will modify the pathbase and path correctly
|
||||
PathBase = string.Empty;
|
||||
Path = originalPath;
|
||||
}
|
||||
|
||||
var cookedUrl = GetCookedUrl();
|
||||
QueryString = cookedUrl.GetQueryString() ?? string.Empty;
|
||||
|
||||
RequestHeaders = new RequestHeaders(this);
|
||||
HttpResponseHeaders = new HeaderCollection();
|
||||
ResponseHeaders = HttpResponseHeaders;
|
||||
|
||||
if (_options.ForwardWindowsAuthentication)
|
||||
{
|
||||
WindowsUser = GetWindowsPrincipal();
|
||||
if (_options.AutomaticAuthentication)
|
||||
{
|
||||
User = WindowsUser;
|
||||
}
|
||||
}
|
||||
|
||||
ResetFeatureCollection();
|
||||
|
||||
if (!_server.IsWebSocketAvailible(_pInProcessHandler))
|
||||
{
|
||||
_currentIHttpUpgradeFeature = null;
|
||||
}
|
||||
|
||||
RequestBody = new IISHttpRequestBody(this);
|
||||
ResponseBody = new IISHttpResponseBody(this);
|
||||
|
||||
Input = new Pipe(new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.ThreadPool, minimumSegmentSize: MinAllocBufferSize));
|
||||
var pipe = new Pipe(
|
||||
new PipeOptions(
|
||||
_memoryPool,
|
||||
readerScheduler: PipeScheduler.ThreadPool,
|
||||
pauseWriterThreshold: PauseWriterThreshold,
|
||||
resumeWriterThreshold: ResumeWriterTheshold,
|
||||
minimumSegmentSize: MinAllocBufferSize));
|
||||
Output = new OutputProducer(pipe);
|
||||
}
|
||||
|
||||
public int StatusCode
|
||||
{
|
||||
|
|
@ -545,12 +510,5 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum WebsocketAvailabilityStatus
|
||||
{
|
||||
Uninitialized = 1,
|
||||
Available,
|
||||
NotAvailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
|
||||
public override async Task<bool> ProcessRequestAsync()
|
||||
{
|
||||
InitializeContext();
|
||||
|
||||
var context = default(TContext);
|
||||
var success = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
{
|
||||
internal class IISHttpServer : IServer
|
||||
{
|
||||
private const string WebSocketVersionString = "WEBSOCKET_VERSION";
|
||||
|
||||
private static NativeMethods.PFN_REQUEST_HANDLER _requestHandler = HandleRequest;
|
||||
private static NativeMethods.PFN_SHUTDOWN_HANDLER _shutdownHandler = HandleShutdown;
|
||||
private static NativeMethods.PFN_ASYNC_COMPLETION _onAsyncCompletion = OnAsyncCompletion;
|
||||
|
|
@ -32,8 +34,28 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
private bool Stopping => _stopping == 1;
|
||||
private int _outstandingRequests;
|
||||
private readonly TaskCompletionSource<object> _shutdownSignal = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
private bool? _websocketAvailable;
|
||||
|
||||
public IFeatureCollection Features { get; } = new FeatureCollection();
|
||||
|
||||
// TODO: Remove pInProcessHandler argument
|
||||
public bool IsWebSocketAvailible(IntPtr pInProcessHandler)
|
||||
{
|
||||
// Check if the Http upgrade feature is available in IIS.
|
||||
// To check this, we can look at the server variable WEBSOCKET_VERSION
|
||||
// And see if there is a version. Same check that Katana did:
|
||||
// https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Host.SystemWeb/OwinAppContext.cs#L125
|
||||
// Actively not locking here as acquiring a lock on every request will hurt perf more than checking the
|
||||
// server variables a few extra times if a bunch of requests hit the server at the same time.
|
||||
if (!_websocketAvailable.HasValue)
|
||||
{
|
||||
_websocketAvailable = NativeMethods.HttpTryGetServerVariable(pInProcessHandler, WebSocketVersionString, out var webSocketsSupported)
|
||||
&& !string.IsNullOrEmpty(webSocketsSupported);
|
||||
}
|
||||
|
||||
return _websocketAvailable.Value;
|
||||
}
|
||||
|
||||
public IISHttpServer(IApplicationLifetime applicationLifetime, IAuthenticationSchemeProvider authentication, IOptions<IISOptions> options)
|
||||
{
|
||||
_applicationLifetime = applicationLifetime;
|
||||
|
|
@ -124,13 +146,17 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
|
||||
var context = server._iisContextFactory.CreateHttpContext(pInProcessHandler);
|
||||
|
||||
var task = Task.Run(() => context.ProcessRequestAsync());
|
||||
|
||||
task.ContinueWith((t, state) => CompleteRequest((IISHttpContext)state, t), context);
|
||||
ThreadPool.QueueUserWorkItem(state => _ = HandleRequest((IISHttpContext)state), context);
|
||||
|
||||
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
|
||||
}
|
||||
|
||||
private static async Task HandleRequest(IISHttpContext context)
|
||||
{
|
||||
var result = await context.ProcessRequestAsync();
|
||||
CompleteRequest(context, result);
|
||||
}
|
||||
|
||||
private static bool HandleShutdown(IntPtr pvRequestContext)
|
||||
{
|
||||
var server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
|
||||
|
|
@ -145,10 +171,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
|||
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
|
||||
}
|
||||
|
||||
private static void CompleteRequest(IISHttpContext context, Task<bool> completedTask)
|
||||
private static void CompleteRequest(IISHttpContext context, bool result)
|
||||
{
|
||||
// Post completion after completing the request to resume the state machine
|
||||
context.PostCompletion(ConvertRequestCompletionResults(completedTask.Result));
|
||||
context.PostCompletion(ConvertRequestCompletionResults(result));
|
||||
|
||||
if (Interlocked.Decrement(ref context.Server._outstandingRequests) == 0 && context.Server.Stopping)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ http_read_request_bytes(
|
|||
_Out_ BOOL* pfCompletionPending
|
||||
)
|
||||
{
|
||||
HRESULT hr;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (pInProcessHandler == NULL)
|
||||
{
|
||||
|
|
@ -206,19 +206,27 @@ http_read_request_bytes(
|
|||
}
|
||||
IHttpRequest *pHttpRequest = (IHttpRequest*)pInProcessHandler->QueryHttpContext()->GetRequest();
|
||||
|
||||
BOOL fAsync = TRUE;
|
||||
|
||||
hr = pHttpRequest->ReadEntityBody(
|
||||
pvBuffer,
|
||||
dwCbBuffer,
|
||||
fAsync,
|
||||
pdwBytesReceived,
|
||||
pfCompletionPending);
|
||||
|
||||
if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
|
||||
// Check if there is anything to read
|
||||
if (pHttpRequest->GetRemainingEntityBytes() > 0)
|
||||
{
|
||||
// We reached the end of the data
|
||||
hr = S_OK;
|
||||
BOOL fAsync = TRUE;
|
||||
hr = pHttpRequest->ReadEntityBody(
|
||||
pvBuffer,
|
||||
dwCbBuffer,
|
||||
fAsync,
|
||||
pdwBytesReceived,
|
||||
pfCompletionPending);
|
||||
|
||||
if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
|
||||
{
|
||||
// We reached the end of the data
|
||||
hr = S_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*pdwBytesReceived = 0;
|
||||
*pfCompletionPending = FALSE;
|
||||
}
|
||||
|
||||
return hr;
|
||||
|
|
|
|||
Loading…
Reference in New Issue