#204 Consolidate unsafe buffer access into NativeRequestContext
This commit is contained in:
parent
3d1dbbaae5
commit
b31ec7d7a7
|
|
@ -108,13 +108,13 @@ namespace Microsoft.Net.Http.Server
|
|||
}
|
||||
else
|
||||
{
|
||||
asyncResult._nativeRequestContext.Reset(0, 0);
|
||||
asyncResult._nativeRequestContext.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asyncResult._nativeRequestContext.Reset(asyncResult._nativeRequestContext.RequestBlob->RequestId, numBytes);
|
||||
asyncResult._nativeRequestContext.Reset(asyncResult._nativeRequestContext.RequestId, numBytes);
|
||||
}
|
||||
|
||||
// We need to issue a new request, either because auth failed, or because our buffer was too small the first time.
|
||||
|
|
@ -166,26 +166,26 @@ namespace Microsoft.Net.Http.Server
|
|||
uint bytesTransferred = 0;
|
||||
statusCode = HttpApi.HttpReceiveHttpRequest(
|
||||
Server.RequestQueue.Handle,
|
||||
_nativeRequestContext.RequestBlob->RequestId,
|
||||
_nativeRequestContext.RequestId,
|
||||
(uint)HttpApi.HTTP_FLAGS.HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY,
|
||||
_nativeRequestContext.RequestBlob,
|
||||
_nativeRequestContext.NativeRequest,
|
||||
_nativeRequestContext.Size,
|
||||
&bytesTransferred,
|
||||
_nativeRequestContext.NativeOverlapped);
|
||||
|
||||
if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER && _nativeRequestContext.RequestBlob->RequestId != 0)
|
||||
if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER && _nativeRequestContext.RequestId != 0)
|
||||
{
|
||||
// we might get this if somebody stole our RequestId,
|
||||
// set RequestId to 0 and start all over again with the buffer we just allocated
|
||||
// BUGBUG: how can someone steal our request ID? seems really bad and in need of fix.
|
||||
_nativeRequestContext.RequestBlob->RequestId = 0;
|
||||
_nativeRequestContext.RequestId = 0;
|
||||
retry = true;
|
||||
}
|
||||
else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
|
||||
{
|
||||
// the buffer was not big enough to fit the headers, we need
|
||||
// to read the RequestId returned, allocate a new buffer of the required size
|
||||
_nativeRequestContext.Reset(_nativeRequestContext.RequestBlob->RequestId, bytesTransferred);
|
||||
_nativeRequestContext.Reset(_nativeRequestContext.RequestId, bytesTransferred);
|
||||
retry = true;
|
||||
}
|
||||
else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS
|
||||
|
|
|
|||
|
|
@ -152,51 +152,5 @@ namespace Microsoft.Net.Http.Server
|
|||
= StringValues.Concat(context.Response.Headers[HttpKnownHeaderNames.WWWAuthenticate], challenges.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
internal static unsafe bool CheckAuthenticated(HttpApi.HTTP_REQUEST_INFO* requestInfo)
|
||||
{
|
||||
if (requestInfo != null
|
||||
&& requestInfo->InfoType == HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth
|
||||
&& requestInfo->pInfo->AuthStatus == HttpApi.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static unsafe ClaimsPrincipal GetUser(HttpApi.HTTP_REQUEST_INFO* requestInfo, int infoCount)
|
||||
{
|
||||
for (int i = 0; i < infoCount; i++)
|
||||
{
|
||||
var info = &requestInfo[i];
|
||||
if (requestInfo != null
|
||||
&& info->InfoType == HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth
|
||||
&& info->pInfo->AuthStatus == HttpApi.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
|
||||
{
|
||||
return new WindowsPrincipal(new WindowsIdentity(info->pInfo->AccessToken,
|
||||
GetAuthTypeFromRequest(info->pInfo->AuthType).ToString()));
|
||||
}
|
||||
}
|
||||
return new ClaimsPrincipal(new ClaimsIdentity()); // Anonymous / !IsAuthenticated
|
||||
}
|
||||
|
||||
private static AuthenticationSchemes GetAuthTypeFromRequest(HttpApi.HTTP_REQUEST_AUTH_TYPE input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeBasic:
|
||||
return AuthenticationSchemes.Basic;
|
||||
// case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeDigest:
|
||||
// return AuthenticationSchemes.Digest;
|
||||
case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNTLM:
|
||||
return AuthenticationSchemes.NTLM;
|
||||
case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNegotiate:
|
||||
return AuthenticationSchemes.Negotiate;
|
||||
case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeKerberos:
|
||||
return AuthenticationSchemes.Kerberos;
|
||||
default:
|
||||
throw new NotImplementedException(input.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="_ListenerAsyncResult.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Net.Http.Server
|
||||
{
|
||||
// Note this type should only be used while the request buffer remains pinned
|
||||
internal class CookedUrl
|
||||
{
|
||||
private readonly HttpApi.HTTP_COOKED_URL _nativeCookedUrl;
|
||||
|
||||
internal CookedUrl(HttpApi.HTTP_COOKED_URL nativeCookedUrl)
|
||||
{
|
||||
_nativeCookedUrl = nativeCookedUrl;
|
||||
}
|
||||
|
||||
internal unsafe string GetFullUrl()
|
||||
{
|
||||
if (_nativeCookedUrl.pFullUrl != null && _nativeCookedUrl.FullUrlLength > 0)
|
||||
{
|
||||
return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pFullUrl, _nativeCookedUrl.FullUrlLength / 2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal unsafe string GetHost()
|
||||
{
|
||||
if (_nativeCookedUrl.pHost != null && _nativeCookedUrl.HostLength > 0)
|
||||
{
|
||||
return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pHost, _nativeCookedUrl.HostLength / 2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal unsafe string GetAbsPath()
|
||||
{
|
||||
if (_nativeCookedUrl.pAbsPath != null && _nativeCookedUrl.AbsPathLength > 0)
|
||||
{
|
||||
return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pAbsPath, _nativeCookedUrl.AbsPathLength / 2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal unsafe string GetQueryString()
|
||||
{
|
||||
if (_nativeCookedUrl.pQueryString != null && _nativeCookedUrl.QueryStringLength > 0)
|
||||
{
|
||||
return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pQueryString, _nativeCookedUrl.QueryStringLength / 2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -868,205 +868,5 @@ namespace Microsoft.Net.Http.Server
|
|||
return supported;
|
||||
}
|
||||
}
|
||||
|
||||
// Server API
|
||||
|
||||
internal static void GetUnknownHeaders(IDictionary<string, StringValues> unknownHeaders, byte[] memoryBlob,
|
||||
int requestOffset, IntPtr originalAddress)
|
||||
{
|
||||
// Return value.
|
||||
fixed (byte* pMemoryBlob = memoryBlob)
|
||||
{
|
||||
HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset);
|
||||
long fixup = pMemoryBlob - (byte*)originalAddress;
|
||||
int index;
|
||||
|
||||
// unknown headers
|
||||
if (request->Headers.UnknownHeaderCount != 0)
|
||||
{
|
||||
HTTP_UNKNOWN_HEADER* pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders);
|
||||
for (index = 0; index < request->Headers.UnknownHeaderCount; index++)
|
||||
{
|
||||
// For unknown headers, when header value is empty, RawValueLength will be 0 and
|
||||
// pRawValue will be null.
|
||||
if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0)
|
||||
{
|
||||
string headerName = HeaderEncoding.GetString(pUnknownHeader->pName + fixup, pUnknownHeader->NameLength);
|
||||
string headerValue;
|
||||
if (pUnknownHeader->pRawValue != null && pUnknownHeader->RawValueLength > 0)
|
||||
{
|
||||
headerValue = HeaderEncoding.GetString(pUnknownHeader->pRawValue + fixup, pUnknownHeader->RawValueLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
headerValue = string.Empty;
|
||||
}
|
||||
// Note that Http.Sys currently collapses all headers of the same name to a single coma seperated string,
|
||||
// so we can just call Set.
|
||||
unknownHeaders[headerName] = headerValue;
|
||||
}
|
||||
pUnknownHeader++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetKnownHeader(HTTP_REQUEST* request, long fixup, int headerIndex)
|
||||
{
|
||||
string header = null;
|
||||
|
||||
HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + headerIndex;
|
||||
// For known headers, when header value is empty, RawValueLength will be 0 and
|
||||
// pRawValue will point to empty string ("\0")
|
||||
if (pKnownHeader->pRawValue != null)
|
||||
{
|
||||
header = HeaderEncoding.GetString(pKnownHeader->pRawValue + fixup, pKnownHeader->RawValueLength);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
internal static string GetKnownHeader(byte[] memoryBlob, int requestOffset, IntPtr originalAddress, int headerIndex)
|
||||
{
|
||||
fixed (byte* pMemoryBlob = memoryBlob)
|
||||
{
|
||||
return GetKnownHeader(
|
||||
(HTTP_REQUEST*)(pMemoryBlob + requestOffset), pMemoryBlob - (byte*)originalAddress, headerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// This requires the HTTP_REQUEST to still be pinned in its original location.
|
||||
internal static unsafe string GetVerb(HTTP_REQUEST* request)
|
||||
{
|
||||
string verb = null;
|
||||
|
||||
if ((int)request->Verb > (int)HTTP_VERB.HttpVerbUnknown && (int)request->Verb < (int)HTTP_VERB.HttpVerbMaximum)
|
||||
{
|
||||
verb = HttpVerbs[(int)request->Verb];
|
||||
}
|
||||
else if (request->Verb == HTTP_VERB.HttpVerbUnknown && request->pUnknownVerb != null)
|
||||
{
|
||||
verb = HeaderEncoding.GetString(request->pUnknownVerb, request->UnknownVerbLength);
|
||||
}
|
||||
|
||||
return verb;
|
||||
}
|
||||
|
||||
internal static uint GetChunks(byte[] memoryBlob, int requestOffset, IntPtr originalAddress,
|
||||
ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size)
|
||||
{
|
||||
// Return value.
|
||||
uint dataRead = 0;
|
||||
fixed (byte* pMemoryBlob = memoryBlob)
|
||||
{
|
||||
HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset);
|
||||
long fixup = pMemoryBlob - (byte*)originalAddress;
|
||||
|
||||
if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1)
|
||||
{
|
||||
HTTP_DATA_CHUNK* pDataChunk = (HTTP_DATA_CHUNK*)(fixup + (byte*)&request->pEntityChunks[dataChunkIndex]);
|
||||
|
||||
fixed (byte* pReadBuffer = buffer)
|
||||
{
|
||||
byte* pTo = &pReadBuffer[offset];
|
||||
|
||||
while (dataChunkIndex < request->EntityChunkCount && dataRead < size)
|
||||
{
|
||||
if (dataChunkOffset >= pDataChunk->fromMemory.BufferLength)
|
||||
{
|
||||
dataChunkOffset = 0;
|
||||
dataChunkIndex++;
|
||||
pDataChunk++;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte* pFrom = (byte*)pDataChunk->fromMemory.pBuffer + dataChunkOffset + fixup;
|
||||
|
||||
uint bytesToRead = pDataChunk->fromMemory.BufferLength - (uint)dataChunkOffset;
|
||||
if (bytesToRead > (uint)size)
|
||||
{
|
||||
bytesToRead = (uint)size;
|
||||
}
|
||||
for (uint i = 0; i < bytesToRead; i++)
|
||||
{
|
||||
*(pTo++) = *(pFrom++);
|
||||
}
|
||||
dataRead += bytesToRead;
|
||||
dataChunkOffset += bytesToRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// we're finished.
|
||||
if (dataChunkIndex == request->EntityChunkCount)
|
||||
{
|
||||
dataChunkIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return dataRead;
|
||||
}
|
||||
|
||||
internal static SocketAddress GetRemoteEndPoint(byte[] memoryBlob, int requestOffset, IntPtr originalAddress)
|
||||
{
|
||||
fixed (byte* pMemoryBlob = memoryBlob)
|
||||
{
|
||||
HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset);
|
||||
return GetEndPoint(memoryBlob, requestOffset, originalAddress, (byte*)request->Address.pRemoteAddress);
|
||||
}
|
||||
}
|
||||
|
||||
internal static SocketAddress GetLocalEndPoint(byte[] memoryBlob, int requestOffset, IntPtr originalAddress)
|
||||
{
|
||||
fixed (byte* pMemoryBlob = memoryBlob)
|
||||
{
|
||||
HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset);
|
||||
return GetEndPoint(memoryBlob, requestOffset, originalAddress, (byte*)request->Address.pLocalAddress);
|
||||
}
|
||||
}
|
||||
|
||||
internal static SocketAddress GetEndPoint(byte[] memoryBlob, int requestOffset, IntPtr originalAddress, byte* source)
|
||||
{
|
||||
fixed (byte* pMemoryBlob = memoryBlob)
|
||||
{
|
||||
IntPtr address = source != null ?
|
||||
(IntPtr)(pMemoryBlob + requestOffset - (byte*)originalAddress + source) : IntPtr.Zero;
|
||||
return CopyOutAddress(address);
|
||||
}
|
||||
}
|
||||
|
||||
private static SocketAddress CopyOutAddress(IntPtr address)
|
||||
{
|
||||
if (address != IntPtr.Zero)
|
||||
{
|
||||
ushort addressFamily = *((ushort*)address);
|
||||
if (addressFamily == (ushort)AddressFamily.InterNetwork)
|
||||
{
|
||||
SocketAddress v4address = new SocketAddress(AddressFamily.InterNetwork, SocketAddress.IPv4AddressSize);
|
||||
fixed (byte* pBuffer = v4address.Buffer)
|
||||
{
|
||||
for (int index = 2; index < SocketAddress.IPv4AddressSize; index++)
|
||||
{
|
||||
pBuffer[index] = ((byte*)address)[index];
|
||||
}
|
||||
}
|
||||
return v4address;
|
||||
}
|
||||
if (addressFamily == (ushort)AddressFamily.InterNetworkV6)
|
||||
{
|
||||
SocketAddress v6address = new SocketAddress(AddressFamily.InterNetworkV6, SocketAddress.IPv6AddressSize);
|
||||
fixed (byte* pBuffer = v6address.Buffer)
|
||||
{
|
||||
for (int index = 2; index < SocketAddress.IPv6AddressSize; index++)
|
||||
{
|
||||
pBuffer[index] = ((byte*)address)[index];
|
||||
}
|
||||
}
|
||||
return v6address;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,12 @@
|
|||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Server
|
||||
{
|
||||
|
|
@ -32,8 +35,8 @@ namespace Microsoft.Net.Http.Server
|
|||
{
|
||||
private const int DefaultBufferSize = 4096;
|
||||
private const int AlignmentPadding = 8;
|
||||
private HttpApi.HTTP_REQUEST* _memoryBlob;
|
||||
private IntPtr _originalBlobAddress;
|
||||
private HttpApi.HTTP_REQUEST* _nativeRequest;
|
||||
private IntPtr _originalBufferAddress;
|
||||
private byte[] _backingBuffer;
|
||||
private int _bufferAlignment;
|
||||
private SafeNativeOverlapped _nativeOverlapped;
|
||||
|
|
@ -42,125 +45,73 @@ namespace Microsoft.Net.Http.Server
|
|||
internal NativeRequestContext(AsyncAcceptContext result)
|
||||
{
|
||||
_acceptResult = result;
|
||||
HttpApi.HTTP_REQUEST* requestBlob = Allocate(0);
|
||||
if (requestBlob == null)
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
_memoryBlob = requestBlob;
|
||||
}
|
||||
AllocateNativeRequest();
|
||||
}
|
||||
|
||||
internal SafeNativeOverlapped NativeOverlapped
|
||||
internal SafeNativeOverlapped NativeOverlapped => _nativeOverlapped;
|
||||
|
||||
internal HttpApi.HTTP_REQUEST* NativeRequest
|
||||
{
|
||||
get
|
||||
{
|
||||
return _nativeOverlapped;
|
||||
Debug.Assert(_nativeRequest != null || _backingBuffer == null, "native request accessed after ReleasePins().");
|
||||
return _nativeRequest;
|
||||
}
|
||||
}
|
||||
|
||||
internal HttpApi.HTTP_REQUEST* RequestBlob
|
||||
private HttpApi.HTTP_REQUEST_V2* NativeRequestV2
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(_memoryBlob != null || _backingBuffer == null, "RequestBlob requested after ReleasePins().");
|
||||
return _memoryBlob;
|
||||
Debug.Assert(_nativeRequest != null || _backingBuffer == null, "native request accessed after ReleasePins().");
|
||||
return (HttpApi.HTTP_REQUEST_V2*)_nativeRequest;
|
||||
}
|
||||
}
|
||||
|
||||
internal byte[] RequestBuffer
|
||||
internal ulong RequestId
|
||||
{
|
||||
get { return NativeRequest->RequestId; }
|
||||
set { NativeRequest->RequestId = value; }
|
||||
}
|
||||
|
||||
internal ulong ConnectionId => NativeRequest->ConnectionId;
|
||||
|
||||
internal HttpApi.HTTP_VERB VerbId => NativeRequest->Verb;
|
||||
|
||||
internal ulong UrlContext => NativeRequest->UrlContext;
|
||||
|
||||
internal ushort UnknownHeaderCount => NativeRequest->Headers.UnknownHeaderCount;
|
||||
|
||||
internal SslStatus SslStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _backingBuffer;
|
||||
return NativeRequest->pSslInfo == null ? SslStatus.Insecure :
|
||||
NativeRequest->pSslInfo->SslClientCertNegotiated == 0 ? SslStatus.NoClientCert :
|
||||
SslStatus.ClientCert;
|
||||
}
|
||||
}
|
||||
|
||||
internal uint Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return (uint)_backingBuffer.Length - AlignmentPadding;
|
||||
}
|
||||
}
|
||||
|
||||
internal int BufferAlignment
|
||||
{
|
||||
get
|
||||
{
|
||||
return _bufferAlignment;
|
||||
}
|
||||
}
|
||||
|
||||
internal IntPtr OriginalBlobAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
HttpApi.HTTP_REQUEST* blob = _memoryBlob;
|
||||
return blob == null ? _originalBlobAddress : (IntPtr)blob;
|
||||
}
|
||||
get { return (uint)_backingBuffer.Length - AlignmentPadding; }
|
||||
}
|
||||
|
||||
// ReleasePins() should be called exactly once. It must be called before Dispose() is called, which means it must be called
|
||||
// before an object (Request) which closes the RequestContext on demand is returned to the application.
|
||||
internal void ReleasePins()
|
||||
{
|
||||
Debug.Assert(_memoryBlob != null || _backingBuffer == null, "RequestContextBase::ReleasePins()|ReleasePins() called twice.");
|
||||
_originalBlobAddress = (IntPtr)_memoryBlob;
|
||||
UnsetBlob();
|
||||
OnReleasePins();
|
||||
}
|
||||
|
||||
private void OnReleasePins()
|
||||
{
|
||||
if (_nativeOverlapped != null)
|
||||
{
|
||||
SafeNativeOverlapped nativeOverlapped = _nativeOverlapped;
|
||||
_nativeOverlapped = null;
|
||||
nativeOverlapped.Dispose();
|
||||
}
|
||||
Debug.Assert(_nativeRequest != null || _backingBuffer == null, "RequestContextBase::ReleasePins()|ReleasePins() called twice.");
|
||||
_originalBufferAddress = (IntPtr)_nativeRequest;
|
||||
_nativeRequest = null;
|
||||
_nativeOverlapped?.Dispose();
|
||||
_nativeOverlapped = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Debug.Assert(_memoryBlob == null, "RequestContextBase::Dispose()|Dispose() called before ReleasePins().");
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
if (_nativeOverlapped != null)
|
||||
{
|
||||
Debug.Assert(!disposing, "AsyncRequestContext::Dispose()|Must call ReleasePins() before calling Dispose().");
|
||||
_nativeOverlapped.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBlob(HttpApi.HTTP_REQUEST* requestBlob)
|
||||
{
|
||||
Debug.Assert(_memoryBlob != null || _backingBuffer == null, "RequestContextBase::Dispose()|SetBlob() called after ReleasePins().");
|
||||
if (requestBlob == null)
|
||||
{
|
||||
UnsetBlob();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_memoryBlob == null)
|
||||
{
|
||||
GC.ReRegisterForFinalize(this);
|
||||
}
|
||||
_memoryBlob = requestBlob;
|
||||
}
|
||||
|
||||
private void UnsetBlob()
|
||||
{
|
||||
if (_memoryBlob != null)
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
_memoryBlob = null;
|
||||
Debug.Assert(_nativeRequest == null, "RequestContextBase::Dispose()|Dispose() called before ReleasePins().");
|
||||
_nativeOverlapped?.Dispose();
|
||||
}
|
||||
|
||||
private void SetBuffer(int size)
|
||||
|
|
@ -170,34 +121,306 @@ namespace Microsoft.Net.Http.Server
|
|||
_backingBuffer = new byte[size + AlignmentPadding];
|
||||
}
|
||||
|
||||
private HttpApi.HTTP_REQUEST* Allocate(uint size)
|
||||
private void AllocateNativeRequest(uint? size = null)
|
||||
{
|
||||
// We can't reuse overlapped objects
|
||||
if (_nativeOverlapped != null)
|
||||
{
|
||||
SafeNativeOverlapped nativeOverlapped = _nativeOverlapped;
|
||||
_nativeOverlapped = null;
|
||||
nativeOverlapped.Dispose();
|
||||
}
|
||||
_nativeOverlapped?.Dispose();
|
||||
|
||||
uint newSize = size != 0 ? size : RequestBuffer == null ? DefaultBufferSize : Size;
|
||||
uint newSize = size.HasValue ? size.Value : _backingBuffer == null ? DefaultBufferSize : Size;
|
||||
SetBuffer(checked((int)newSize));
|
||||
var boundHandle = _acceptResult.Server.RequestQueue.BoundHandle;
|
||||
_nativeOverlapped = new SafeNativeOverlapped(boundHandle,
|
||||
boundHandle.AllocateNativeOverlapped(AsyncAcceptContext.IOCallback, _acceptResult, RequestBuffer));
|
||||
boundHandle.AllocateNativeOverlapped(AsyncAcceptContext.IOCallback, _acceptResult, _backingBuffer));
|
||||
|
||||
// HttpReceiveHttpRequest expects the request pointer to be 8-byte-aligned or it fails. On ARM
|
||||
// CLR creates buffers that are 4-byte-aligned so we need force 8-byte alignment.
|
||||
var requestAddress = Marshal.UnsafeAddrOfPinnedArrayElement(RequestBuffer, 0);
|
||||
var requestAddress = Marshal.UnsafeAddrOfPinnedArrayElement(_backingBuffer, 0);
|
||||
_bufferAlignment = (int)(requestAddress.ToInt64() & 0x07);
|
||||
|
||||
return (HttpApi.HTTP_REQUEST*)(requestAddress + _bufferAlignment);
|
||||
_nativeRequest = (HttpApi.HTTP_REQUEST*)(requestAddress + _bufferAlignment);
|
||||
}
|
||||
|
||||
internal void Reset(ulong requestId, uint size)
|
||||
internal void Reset(ulong requestId = 0, uint? size = null)
|
||||
{
|
||||
SetBlob(Allocate(size));
|
||||
RequestBlob->RequestId = requestId;
|
||||
Debug.Assert(_nativeRequest != null || _backingBuffer == null, "RequestContextBase::Dispose()|SetNativeRequest() called after ReleasePins().");
|
||||
AllocateNativeRequest(size);
|
||||
RequestId = requestId;
|
||||
}
|
||||
|
||||
// These methods require the HTTP_REQUEST to still be pinned in its original location.
|
||||
|
||||
internal string GetVerb()
|
||||
{
|
||||
var verb = NativeRequest->Verb;
|
||||
if (verb > HttpApi.HTTP_VERB.HttpVerbUnknown && verb < HttpApi.HTTP_VERB.HttpVerbMaximum)
|
||||
{
|
||||
return HttpApi.HttpVerbs[(int)verb];
|
||||
}
|
||||
else if (verb == HttpApi.HTTP_VERB.HttpVerbUnknown && NativeRequest->pUnknownVerb != null)
|
||||
{
|
||||
return HeaderEncoding.GetString(NativeRequest->pUnknownVerb, NativeRequest->UnknownVerbLength);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal string GetRawUrl()
|
||||
{
|
||||
if (NativeRequest->pRawUrl != null && NativeRequest->RawUrlLength > 0)
|
||||
{
|
||||
return Marshal.PtrToStringAnsi((IntPtr)NativeRequest->pRawUrl, NativeRequest->RawUrlLength);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal CookedUrl GetCookedUrl()
|
||||
{
|
||||
return new CookedUrl(NativeRequest->CookedUrl);
|
||||
}
|
||||
|
||||
internal Version GetVersion()
|
||||
{
|
||||
var major = NativeRequest->Version.MajorVersion;
|
||||
var minor = NativeRequest->Version.MinorVersion;
|
||||
if (major == 1 && minor == 1)
|
||||
{
|
||||
return Constants.V1_1;
|
||||
}
|
||||
else if (major == 1 && minor == 0)
|
||||
{
|
||||
return Constants.V1_0;
|
||||
}
|
||||
return new Version(major, minor);
|
||||
}
|
||||
|
||||
internal bool CheckAuthenticated()
|
||||
{
|
||||
var requestInfo = NativeRequestV2->pRequestInfo;
|
||||
var infoCount = NativeRequestV2->RequestInfoCount;
|
||||
|
||||
for (int i = 0; i < infoCount; i++)
|
||||
{
|
||||
var info = &requestInfo[i];
|
||||
if (info != null
|
||||
&& info->InfoType == HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth
|
||||
&& info->pInfo->AuthStatus == HttpApi.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal ClaimsPrincipal GetUser()
|
||||
{
|
||||
var requestInfo = NativeRequestV2->pRequestInfo;
|
||||
var infoCount = NativeRequestV2->RequestInfoCount;
|
||||
|
||||
for (int i = 0; i < infoCount; i++)
|
||||
{
|
||||
var info = &requestInfo[i];
|
||||
if (info != null
|
||||
&& info->InfoType == HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth
|
||||
&& info->pInfo->AuthStatus == HttpApi.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
|
||||
{
|
||||
return new WindowsPrincipal(new WindowsIdentity(info->pInfo->AccessToken,
|
||||
GetAuthTypeFromRequest(info->pInfo->AuthType).ToString()));
|
||||
}
|
||||
}
|
||||
return new ClaimsPrincipal(new ClaimsIdentity()); // Anonymous / !IsAuthenticated
|
||||
}
|
||||
|
||||
private static AuthenticationSchemes GetAuthTypeFromRequest(HttpApi.HTTP_REQUEST_AUTH_TYPE input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeBasic:
|
||||
return AuthenticationSchemes.Basic;
|
||||
// case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeDigest:
|
||||
// return AuthenticationSchemes.Digest;
|
||||
case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNTLM:
|
||||
return AuthenticationSchemes.NTLM;
|
||||
case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNegotiate:
|
||||
return AuthenticationSchemes.Negotiate;
|
||||
case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeKerberos:
|
||||
return AuthenticationSchemes.Kerberos;
|
||||
default:
|
||||
throw new NotImplementedException(input.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// These methods are for accessing the request structure after it has been unpinned. They need to adjust addresses
|
||||
// in case GC has moved the original object.
|
||||
|
||||
internal string GetKnownHeader(HttpSysRequestHeader header)
|
||||
{
|
||||
fixed (byte* pMemoryBlob = _backingBuffer)
|
||||
{
|
||||
var request = (HttpApi.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment);
|
||||
long fixup = pMemoryBlob - (byte*)_originalBufferAddress;
|
||||
int headerIndex = (int)header;
|
||||
string value = null;
|
||||
|
||||
HttpApi.HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + headerIndex;
|
||||
// For known headers, when header value is empty, RawValueLength will be 0 and
|
||||
// pRawValue will point to empty string ("\0")
|
||||
if (pKnownHeader->pRawValue != null)
|
||||
{
|
||||
value = HeaderEncoding.GetString(pKnownHeader->pRawValue + fixup, pKnownHeader->RawValueLength);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
internal void GetUnknownHeaders(IDictionary<string, StringValues> unknownHeaders)
|
||||
{
|
||||
// Return value.
|
||||
fixed (byte* pMemoryBlob = _backingBuffer)
|
||||
{
|
||||
var request = (HttpApi.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment);
|
||||
long fixup = pMemoryBlob - (byte*)_originalBufferAddress;
|
||||
int index;
|
||||
|
||||
// unknown headers
|
||||
if (request->Headers.UnknownHeaderCount != 0)
|
||||
{
|
||||
var pUnknownHeader = (HttpApi.HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders);
|
||||
for (index = 0; index < request->Headers.UnknownHeaderCount; index++)
|
||||
{
|
||||
// For unknown headers, when header value is empty, RawValueLength will be 0 and
|
||||
// pRawValue will be null.
|
||||
if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0)
|
||||
{
|
||||
var headerName = HeaderEncoding.GetString(pUnknownHeader->pName + fixup, pUnknownHeader->NameLength);
|
||||
string headerValue;
|
||||
if (pUnknownHeader->pRawValue != null && pUnknownHeader->RawValueLength > 0)
|
||||
{
|
||||
headerValue = HeaderEncoding.GetString(pUnknownHeader->pRawValue + fixup, pUnknownHeader->RawValueLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
headerValue = string.Empty;
|
||||
}
|
||||
// Note that Http.Sys currently collapses all headers of the same name to a single coma separated string,
|
||||
// so we can just call Set.
|
||||
unknownHeaders[headerName] = headerValue;
|
||||
}
|
||||
pUnknownHeader++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal SocketAddress GetRemoteEndPoint()
|
||||
{
|
||||
return GetEndPoint(localEndpoint: false);
|
||||
}
|
||||
|
||||
internal SocketAddress GetLocalEndPoint()
|
||||
{
|
||||
return GetEndPoint(localEndpoint: true);
|
||||
}
|
||||
|
||||
private SocketAddress GetEndPoint(bool localEndpoint)
|
||||
{
|
||||
fixed (byte* pMemoryBlob = _backingBuffer)
|
||||
{
|
||||
var request = (HttpApi.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment);
|
||||
var source = localEndpoint ? (byte*)request->Address.pLocalAddress : (byte*)request->Address.pRemoteAddress;
|
||||
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var address = (IntPtr)(pMemoryBlob + _bufferAlignment - (byte*)_originalBufferAddress + source);
|
||||
return CopyOutAddress(address);
|
||||
}
|
||||
}
|
||||
|
||||
private static SocketAddress CopyOutAddress(IntPtr address)
|
||||
{
|
||||
ushort addressFamily = *((ushort*)address);
|
||||
if (addressFamily == (ushort)AddressFamily.InterNetwork)
|
||||
{
|
||||
var v4address = new SocketAddress(AddressFamily.InterNetwork, SocketAddress.IPv4AddressSize);
|
||||
fixed (byte* pBuffer = v4address.Buffer)
|
||||
{
|
||||
for (int index = 2; index < SocketAddress.IPv4AddressSize; index++)
|
||||
{
|
||||
pBuffer[index] = ((byte*)address)[index];
|
||||
}
|
||||
}
|
||||
return v4address;
|
||||
}
|
||||
if (addressFamily == (ushort)AddressFamily.InterNetworkV6)
|
||||
{
|
||||
var v6address = new SocketAddress(AddressFamily.InterNetworkV6, SocketAddress.IPv6AddressSize);
|
||||
fixed (byte* pBuffer = v6address.Buffer)
|
||||
{
|
||||
for (int index = 2; index < SocketAddress.IPv6AddressSize; index++)
|
||||
{
|
||||
pBuffer[index] = ((byte*)address)[index];
|
||||
}
|
||||
}
|
||||
return v6address;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal uint GetChunks(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size)
|
||||
{
|
||||
// Return value.
|
||||
uint dataRead = 0;
|
||||
fixed (byte* pMemoryBlob = _backingBuffer)
|
||||
{
|
||||
var request = (HttpApi.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment);
|
||||
long fixup = pMemoryBlob - (byte*)_originalBufferAddress;
|
||||
|
||||
if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1)
|
||||
{
|
||||
var pDataChunk = (HttpApi.HTTP_DATA_CHUNK*)(fixup + (byte*)&request->pEntityChunks[dataChunkIndex]);
|
||||
|
||||
fixed (byte* pReadBuffer = buffer)
|
||||
{
|
||||
byte* pTo = &pReadBuffer[offset];
|
||||
|
||||
while (dataChunkIndex < request->EntityChunkCount && dataRead < size)
|
||||
{
|
||||
if (dataChunkOffset >= pDataChunk->fromMemory.BufferLength)
|
||||
{
|
||||
dataChunkOffset = 0;
|
||||
dataChunkIndex++;
|
||||
pDataChunk++;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte* pFrom = (byte*)pDataChunk->fromMemory.pBuffer + dataChunkOffset + fixup;
|
||||
|
||||
uint bytesToRead = pDataChunk->fromMemory.BufferLength - (uint)dataChunkOffset;
|
||||
if (bytesToRead > (uint)size)
|
||||
{
|
||||
bytesToRead = (uint)size;
|
||||
}
|
||||
for (uint i = 0; i < bytesToRead; i++)
|
||||
{
|
||||
*(pTo++) = *(pFrom++);
|
||||
}
|
||||
dataRead += bytesToRead;
|
||||
dataChunkOffset += bytesToRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// we're finished.
|
||||
if (dataChunkIndex == request->EntityChunkCount)
|
||||
{
|
||||
dataChunkIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return dataRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ using System;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
|
|
@ -51,46 +50,27 @@ namespace Microsoft.Net.Http.Server
|
|||
|
||||
private bool _isDisposed = false;
|
||||
|
||||
internal unsafe Request(RequestContext requestContext, NativeRequestContext memoryBlob)
|
||||
internal Request(RequestContext requestContext, NativeRequestContext nativeRequestContext)
|
||||
{
|
||||
// TODO: Verbose log
|
||||
RequestContext = requestContext;
|
||||
_nativeRequestContext = memoryBlob;
|
||||
_nativeRequestContext = nativeRequestContext;
|
||||
_contentBoundaryType = BoundaryType.None;
|
||||
|
||||
// Set up some of these now to avoid refcounting on memory blob later.
|
||||
RequestId = memoryBlob.RequestBlob->RequestId;
|
||||
UConnectionId = memoryBlob.RequestBlob->ConnectionId;
|
||||
SslStatus = memoryBlob.RequestBlob->pSslInfo == null ? SslStatus.Insecure :
|
||||
memoryBlob.RequestBlob->pSslInfo->SslClientCertNegotiated == 0 ? SslStatus.NoClientCert :
|
||||
SslStatus.ClientCert;
|
||||
RequestId = nativeRequestContext.RequestId;
|
||||
UConnectionId = nativeRequestContext.ConnectionId;
|
||||
SslStatus = nativeRequestContext.SslStatus;
|
||||
|
||||
KnownMethod = memoryBlob.RequestBlob->Verb;
|
||||
Method = HttpApi.GetVerb(memoryBlob.RequestBlob);
|
||||
KnownMethod = nativeRequestContext.VerbId;
|
||||
Method = _nativeRequestContext.GetVerb();
|
||||
|
||||
if (memoryBlob.RequestBlob->pRawUrl != null && memoryBlob.RequestBlob->RawUrlLength > 0)
|
||||
{
|
||||
RawUrl = Marshal.PtrToStringAnsi((IntPtr)memoryBlob.RequestBlob->pRawUrl, memoryBlob.RequestBlob->RawUrlLength);
|
||||
}
|
||||
RawUrl = nativeRequestContext.GetRawUrl();
|
||||
|
||||
HttpApi.HTTP_COOKED_URL cookedUrl = memoryBlob.RequestBlob->CookedUrl;
|
||||
if (cookedUrl.pHost != null && cookedUrl.HostLength > 0)
|
||||
{
|
||||
// TODO: Unused
|
||||
// _cookedUrlHost = Marshal.PtrToStringUni((IntPtr)cookedUrl.pHost, cookedUrl.HostLength / 2);
|
||||
}
|
||||
var cookedUrlPath = string.Empty;
|
||||
if (cookedUrl.pAbsPath != null && cookedUrl.AbsPathLength > 0)
|
||||
{
|
||||
cookedUrlPath = Marshal.PtrToStringUni((IntPtr)cookedUrl.pAbsPath, cookedUrl.AbsPathLength / 2);
|
||||
}
|
||||
QueryString = string.Empty;
|
||||
if (cookedUrl.pQueryString != null && cookedUrl.QueryStringLength > 0)
|
||||
{
|
||||
QueryString = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
|
||||
}
|
||||
var cookedUrl = nativeRequestContext.GetCookedUrl();
|
||||
var cookedUrlPath = cookedUrl.GetAbsPath() ?? string.Empty;
|
||||
QueryString = cookedUrl.GetQueryString() ?? string.Empty;
|
||||
|
||||
var prefix = requestContext.Server.Settings.UrlPrefixes.GetPrefix((int)memoryBlob.RequestBlob->UrlContext);
|
||||
var prefix = requestContext.Server.Settings.UrlPrefixes.GetPrefix((int)nativeRequestContext.UrlContext);
|
||||
var originalPath = RequestUriBuilder.GetRequestPath(RawUrl, cookedUrlPath, RequestContext.Logger);
|
||||
|
||||
// 'OPTIONS * HTTP/1.1'
|
||||
|
|
@ -114,25 +94,11 @@ namespace Microsoft.Net.Http.Server
|
|||
Path = originalPath.Substring(prefix.Path.Length - 1);
|
||||
}
|
||||
|
||||
int major = memoryBlob.RequestBlob->Version.MajorVersion;
|
||||
int minor = memoryBlob.RequestBlob->Version.MinorVersion;
|
||||
if (major == 1 && minor == 1)
|
||||
{
|
||||
ProtocolVersion = Constants.V1_1;
|
||||
}
|
||||
else if (major == 1 && minor == 0)
|
||||
{
|
||||
ProtocolVersion = Constants.V1_0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProtocolVersion = new Version(major, minor);
|
||||
}
|
||||
ProtocolVersion = _nativeRequestContext.GetVersion();
|
||||
|
||||
Headers = new HeaderCollection(new RequestHeaders(_nativeRequestContext));
|
||||
|
||||
var requestV2 = (HttpApi.HTTP_REQUEST_V2*)memoryBlob.RequestBlob;
|
||||
User = AuthenticationManager.GetUser(requestV2->pRequestInfo, requestV2->RequestInfoCount);
|
||||
User = nativeRequestContext.GetUser();
|
||||
|
||||
// GetTlsTokenBindingInfo(); TODO: https://github.com/aspnet/WebListener/issues/231
|
||||
|
||||
|
|
@ -141,33 +107,6 @@ namespace Microsoft.Net.Http.Server
|
|||
// TODO: Verbose log parameters
|
||||
}
|
||||
|
||||
internal byte[] RequestBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _nativeRequestContext.RequestBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
internal int BufferAlignment
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _nativeRequestContext.BufferAlignment;
|
||||
}
|
||||
}
|
||||
|
||||
internal IntPtr OriginalBlobAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _nativeRequestContext.OriginalBlobAddress;
|
||||
}
|
||||
}
|
||||
|
||||
internal ulong UConnectionId { get; }
|
||||
|
||||
// No ulongs in public APIs...
|
||||
|
|
@ -260,7 +199,7 @@ namespace Microsoft.Net.Http.Server
|
|||
{
|
||||
if (_remoteEndPoint == null)
|
||||
{
|
||||
_remoteEndPoint = HttpApi.GetRemoteEndPoint(RequestBuffer, BufferAlignment, OriginalBlobAddress);
|
||||
_remoteEndPoint = _nativeRequestContext.GetRemoteEndPoint();
|
||||
}
|
||||
|
||||
return _remoteEndPoint;
|
||||
|
|
@ -273,7 +212,7 @@ namespace Microsoft.Net.Http.Server
|
|||
{
|
||||
if (_localEndPoint == null)
|
||||
{
|
||||
_localEndPoint = HttpApi.GetLocalEndPoint(RequestBuffer, BufferAlignment, OriginalBlobAddress);
|
||||
_localEndPoint = _nativeRequestContext.GetLocalEndPoint();
|
||||
}
|
||||
|
||||
return _localEndPoint;
|
||||
|
|
@ -356,6 +295,7 @@ namespace Microsoft.Net.Http.Server
|
|||
// Key: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_ENABLE_TOKEN_BINDING
|
||||
// Value: "iexplore.exe"=dword:00000001
|
||||
// TODO: https://github.com/aspnet/WebListener/issues/231
|
||||
// TODO: https://github.com/aspnet/WebListener/issues/204 Move to NativeRequestContext
|
||||
/*
|
||||
private unsafe void GetTlsTokenBindingInfo()
|
||||
{
|
||||
|
|
@ -371,6 +311,11 @@ namespace Microsoft.Net.Http.Server
|
|||
}
|
||||
}
|
||||
*/
|
||||
internal uint GetChunks(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size)
|
||||
{
|
||||
return _nativeRequestContext.GetChunks(ref dataChunkIndex, ref dataChunkOffset, buffer, offset, size);
|
||||
}
|
||||
|
||||
// should only be called from RequestContext
|
||||
internal void Dispose()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -85,14 +85,12 @@ namespace Microsoft.Net.Http.Server
|
|||
|
||||
private string GetKnownHeader(HttpSysRequestHeader header)
|
||||
{
|
||||
return HttpApi.GetKnownHeader(_requestMemoryBlob.RequestBuffer,
|
||||
_requestMemoryBlob.BufferAlignment, _requestMemoryBlob.OriginalBlobAddress, (int)header);
|
||||
return _requestMemoryBlob.GetKnownHeader(header);
|
||||
}
|
||||
|
||||
private void GetUnknownHeaders(IDictionary<string, StringValues> extra)
|
||||
{
|
||||
HttpApi.GetUnknownHeaders(extra, _requestMemoryBlob.RequestBuffer,
|
||||
_requestMemoryBlob.BufferAlignment, _requestMemoryBlob.OriginalBlobAddress);
|
||||
_requestMemoryBlob.GetUnknownHeaders(extra);
|
||||
}
|
||||
|
||||
void IDictionary<string, StringValues>.Add(string key, StringValues value)
|
||||
|
|
|
|||
|
|
@ -154,9 +154,7 @@ namespace Microsoft.Net.Http.Server
|
|||
|
||||
if (_dataChunkIndex != -1)
|
||||
{
|
||||
dataRead = HttpApi.GetChunks(_requestContext.Request.RequestBuffer,
|
||||
_requestContext.Request.BufferAlignment, _requestContext.Request.OriginalBlobAddress,
|
||||
ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
|
||||
dataRead = _requestContext.Request.GetChunks(ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
|
||||
}
|
||||
|
||||
if (_dataChunkIndex == -1 && dataRead < size)
|
||||
|
|
@ -232,8 +230,7 @@ namespace Microsoft.Net.Http.Server
|
|||
uint dataRead = 0;
|
||||
if (_dataChunkIndex != -1)
|
||||
{
|
||||
dataRead = HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.BufferAlignment,
|
||||
_requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
|
||||
dataRead = _requestContext.Request.GetChunks(ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
|
||||
|
||||
if (_dataChunkIndex != -1 && dataRead == size)
|
||||
{
|
||||
|
|
@ -350,8 +347,7 @@ namespace Microsoft.Net.Http.Server
|
|||
uint dataRead = 0;
|
||||
if (_dataChunkIndex != -1)
|
||||
{
|
||||
dataRead = HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.BufferAlignment,
|
||||
_requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
|
||||
dataRead = _requestContext.Request.GetChunks(ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
|
||||
if (_dataChunkIndex != -1 && dataRead == size)
|
||||
{
|
||||
UpdateAfterRead(UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS, dataRead);
|
||||
|
|
|
|||
|
|
@ -319,9 +319,9 @@ namespace Microsoft.Net.Http.Server
|
|||
internal unsafe bool ValidateRequest(NativeRequestContext requestMemory)
|
||||
{
|
||||
// Block potential DOS attacks
|
||||
if (requestMemory.RequestBlob->Headers.UnknownHeaderCount > UnknownHeaderLimit)
|
||||
if (requestMemory.UnknownHeaderCount > UnknownHeaderLimit)
|
||||
{
|
||||
SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.BadRequest, authChallenges: null);
|
||||
SendError(requestMemory.RequestId, HttpStatusCode.BadRequest, authChallenges: null);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -329,10 +329,9 @@ namespace Microsoft.Net.Http.Server
|
|||
|
||||
internal unsafe bool ValidateAuth(NativeRequestContext requestMemory)
|
||||
{
|
||||
var requestV2 = (HttpApi.HTTP_REQUEST_V2*)requestMemory.RequestBlob;
|
||||
if (!Settings.Authentication.AllowAnonymous && !AuthenticationManager.CheckAuthenticated(requestV2->pRequestInfo))
|
||||
if (!Settings.Authentication.AllowAnonymous && !requestMemory.CheckAuthenticated())
|
||||
{
|
||||
SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.Unauthorized,
|
||||
SendError(requestMemory.RequestId, HttpStatusCode.Unauthorized,
|
||||
AuthenticationManager.GenerateChallenges(Settings.Authentication.Schemes));
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Server.WebListener.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Theory(Skip = "https://github.com/aspnet/WebListener/issues/210")]
|
||||
[InlineData("Set-cookie")]
|
||||
[InlineData("vary")]
|
||||
[InlineData("pragma")]
|
||||
|
|
@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.Server.WebListener.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact(Skip = "https://github.com/aspnet/WebListener/issues/210")]
|
||||
public async Task Caching_ExpiresWithoutPublic_NotCached()
|
||||
{
|
||||
var requestCount = 1;
|
||||
|
|
|
|||
Loading…
Reference in New Issue