diff --git a/src/Microsoft.Net.Http.Server/NativeInterop/UnsafeNativeMethods.cs b/src/Microsoft.Net.Http.Server/NativeInterop/UnsafeNativeMethods.cs index 8329a27cab..b4c732e1ee 100644 --- a/src/Microsoft.Net.Http.Server/NativeInterop/UnsafeNativeMethods.cs +++ b/src/Microsoft.Net.Http.Server/NativeInterop/UnsafeNativeMethods.cs @@ -1049,12 +1049,13 @@ namespace Microsoft.Net.Http.Server // Server API - internal static void GetUnknownHeaders(IDictionary unknownHeaders, byte[] memoryBlob, IntPtr originalAddress) + internal static void GetUnknownHeaders(IDictionary unknownHeaders, byte[] memoryBlob, + int requestOffset, IntPtr originalAddress) { // Return value. fixed (byte* pMemoryBlob = memoryBlob) { - HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; + HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset); long fixup = pMemoryBlob - (byte*)originalAddress; int index; @@ -1064,7 +1065,7 @@ namespace Microsoft.Net.Http.Server 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 + // For unknown headers, when header value is empty, RawValueLength will be 0 and // pRawValue will be null. if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) { @@ -1093,7 +1094,7 @@ namespace Microsoft.Net.Http.Server string header = null; HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + headerIndex; - // For known headers, when header value is empty, RawValueLength will be 0 and + // For known headers, when header value is empty, RawValueLength will be 0 and // pRawValue will point to empty string ("\0") if (pKnownHeader->pRawValue != null) { @@ -1103,11 +1104,12 @@ namespace Microsoft.Net.Http.Server return header; } - internal static string GetKnownHeader(byte[] memoryBlob, IntPtr originalAddress, int headerIndex) + internal static string GetKnownHeader(byte[] memoryBlob, int requestOffset, IntPtr originalAddress, int headerIndex) { fixed (byte* pMemoryBlob = memoryBlob) { - return GetKnownHeader((HTTP_REQUEST*)pMemoryBlob, pMemoryBlob - (byte*)originalAddress, headerIndex); + return GetKnownHeader( + (HTTP_REQUEST*)(pMemoryBlob + requestOffset), pMemoryBlob - (byte*)originalAddress, headerIndex); } } @@ -1127,21 +1129,21 @@ namespace Microsoft.Net.Http.Server return verb; } - internal static unsafe string GetVerb(byte[] memoryBlob, IntPtr originalAddress) + internal static unsafe string GetVerb(byte[] memoryBlob, int requestOffset, IntPtr originalAddress) { fixed (byte* pMemoryBlob = memoryBlob) { - return GetVerb((HTTP_REQUEST*)pMemoryBlob, pMemoryBlob - (byte*)originalAddress); + return GetVerb((HTTP_REQUEST*)(pMemoryBlob + requestOffset), pMemoryBlob - (byte*)originalAddress); } } - internal static HTTP_VERB GetKnownVerb(byte[] memoryBlob, IntPtr originalAddress) + internal static HTTP_VERB GetKnownVerb(byte[] memoryBlob, int requestOffset, IntPtr originalAddress) { // Return value. HTTP_VERB verb = HTTP_VERB.HttpVerbUnknown; fixed (byte* pMemoryBlob = memoryBlob) { - HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; + HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset); if ((int)request->Verb > (int)HTTP_VERB.HttpVerbUnparsed && (int)request->Verb < (int)HTTP_VERB.HttpVerbMaximum) { verb = request->Verb; @@ -1151,13 +1153,14 @@ namespace Microsoft.Net.Http.Server return verb; } - internal static uint GetChunks(byte[] memoryBlob, IntPtr originalAddress, ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size) + 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; + HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset); long fixup = pMemoryBlob - (byte*)originalAddress; if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1) @@ -1205,30 +1208,30 @@ namespace Microsoft.Net.Http.Server return dataRead; } - internal static SocketAddress GetRemoteEndPoint(byte[] memoryBlob, IntPtr originalAddress) + internal static SocketAddress GetRemoteEndPoint(byte[] memoryBlob, int requestOffset, IntPtr originalAddress) { fixed (byte* pMemoryBlob = memoryBlob) { - HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; - return GetEndPoint(memoryBlob, originalAddress, (byte*)request->Address.pRemoteAddress); + HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset); + return GetEndPoint(memoryBlob, requestOffset, originalAddress, (byte*)request->Address.pRemoteAddress); } } - internal static SocketAddress GetLocalEndPoint(byte[] memoryBlob, IntPtr originalAddress) + internal static SocketAddress GetLocalEndPoint(byte[] memoryBlob, int requestOffset, IntPtr originalAddress) { fixed (byte* pMemoryBlob = memoryBlob) { - HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; - return GetEndPoint(memoryBlob, originalAddress, (byte*)request->Address.pLocalAddress); + HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset); + return GetEndPoint(memoryBlob, requestOffset, originalAddress, (byte*)request->Address.pLocalAddress); } } - internal static SocketAddress GetEndPoint(byte[] memoryBlob, IntPtr originalAddress, byte* source) + internal static SocketAddress GetEndPoint(byte[] memoryBlob, int requestOffset, IntPtr originalAddress, byte* source) { fixed (byte* pMemoryBlob = memoryBlob) { IntPtr address = source != null ? - (IntPtr)(pMemoryBlob - (byte*)originalAddress + source) : IntPtr.Zero; + (IntPtr)(pMemoryBlob + requestOffset - (byte*)originalAddress + source) : IntPtr.Zero; return CopyOutAddress(address); } } diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/NativeRequestContext.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/NativeRequestContext.cs index 30ada89d29..cfc100f31f 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/NativeRequestContext.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/NativeRequestContext.cs @@ -31,9 +31,11 @@ namespace Microsoft.Net.Http.Server internal unsafe class NativeRequestContext : IDisposable { private const int DefaultBufferSize = 4096; + private const int AlignmentPadding = 8; private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* _memoryBlob; private IntPtr _originalBlobAddress; private byte[] _backingBuffer; + private int _bufferAlignment; private SafeNativeOverlapped _nativeOverlapped; private AsyncAcceptContext _acceptResult; @@ -80,7 +82,15 @@ namespace Microsoft.Net.Http.Server { get { - return (uint)_backingBuffer.Length; + return (uint)_backingBuffer.Length - AlignmentPadding; + } + } + + internal int BufferAlignment + { + get + { + return _bufferAlignment; } } @@ -89,7 +99,7 @@ namespace Microsoft.Net.Http.Server get { UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* blob = _memoryBlob; - return (blob == null ? _originalBlobAddress : (IntPtr)blob); + return blob == null ? _originalBlobAddress : (IntPtr)blob; } } @@ -155,12 +165,13 @@ namespace Microsoft.Net.Http.Server private void SetBuffer(int size) { - _backingBuffer = size == 0 ? null : new byte[size]; + Debug.Assert(size != 0, "unexpected size"); + + _backingBuffer = new byte[size + AlignmentPadding]; } private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* Allocate(uint size) { - uint newSize = size != 0 ? size : RequestBuffer == null ? DefaultBufferSize : Size; // We can't reuse overlapped objects if (_nativeOverlapped != null) { @@ -168,15 +179,19 @@ namespace Microsoft.Net.Http.Server _nativeOverlapped = null; nativeOverlapped.Dispose(); } - if (_nativeOverlapped == null) - { - SetBuffer(checked((int)newSize)); - var boundHandle = _acceptResult.Server.BoundHandle; - _nativeOverlapped = new SafeNativeOverlapped(boundHandle, - boundHandle.AllocateNativeOverlapped(AsyncAcceptContext.IOCallback, _acceptResult, RequestBuffer)); - return (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST*)Marshal.UnsafeAddrOfPinnedArrayElement(RequestBuffer, 0); - } - return RequestBlob; + + uint newSize = size != 0 ? size : RequestBuffer == null ? DefaultBufferSize : Size; + SetBuffer(checked((int)newSize)); + var boundHandle = _acceptResult.Server.BoundHandle; + _nativeOverlapped = new SafeNativeOverlapped(boundHandle, + boundHandle.AllocateNativeOverlapped(AsyncAcceptContext.IOCallback, _acceptResult, RequestBuffer)); + + // 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); + _bufferAlignment = (int)(requestAddress.ToInt64() & 0x07); + + return (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST*)(requestAddress + _bufferAlignment); } internal void Reset(ulong requestId, uint size) diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/Request.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/Request.cs index d2671ae7a2..72bc826284 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/Request.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/Request.cs @@ -141,7 +141,7 @@ namespace Microsoft.Net.Http.Server _httpVersion = new Version(major, minor); } - _httpMethod = UnsafeNclNativeMethods.HttpApi.GetVerb(RequestBuffer, OriginalBlobAddress); + _httpMethod = UnsafeNclNativeMethods.HttpApi.GetVerb(RequestBuffer, BufferAlignment, OriginalBlobAddress); _headers = new HeaderCollection(new RequestHeaders(_nativeRequestContext)); var requestV2 = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)memoryBlob.RequestBlob; @@ -190,6 +190,15 @@ namespace Microsoft.Net.Http.Server } } + internal int BufferAlignment + { + get + { + CheckDisposed(); + return _nativeRequestContext.BufferAlignment; + } + } + internal IntPtr OriginalBlobAddress { get @@ -304,7 +313,7 @@ namespace Microsoft.Net.Http.Server return _rawUrl; } } - + public Version ProtocolVersion { get @@ -329,7 +338,7 @@ namespace Microsoft.Net.Http.Server { if (_remoteEndPoint == null) { - _remoteEndPoint = UnsafeNclNativeMethods.HttpApi.GetRemoteEndPoint(RequestBuffer, OriginalBlobAddress); + _remoteEndPoint = UnsafeNclNativeMethods.HttpApi.GetRemoteEndPoint(RequestBuffer, BufferAlignment, OriginalBlobAddress); } return _remoteEndPoint; @@ -342,7 +351,7 @@ namespace Microsoft.Net.Http.Server { if (_localEndPoint == null) { - _localEndPoint = UnsafeNclNativeMethods.HttpApi.GetLocalEndPoint(RequestBuffer, OriginalBlobAddress); + _localEndPoint = UnsafeNclNativeMethods.HttpApi.GetLocalEndPoint(RequestBuffer, BufferAlignment, OriginalBlobAddress); } return _localEndPoint; @@ -406,7 +415,7 @@ namespace Microsoft.Net.Http.Server internal UnsafeNclNativeMethods.HttpApi.HTTP_VERB GetKnownMethod() { - return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, OriginalBlobAddress); + return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, BufferAlignment, OriginalBlobAddress); } // Populates the client certificate. The result may be null if there is no client cert. diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/RequestHeaders.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestHeaders.cs index d35e06cc56..1680f723d5 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/RequestHeaders.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestHeaders.cs @@ -86,13 +86,13 @@ namespace Microsoft.Net.Http.Server private string GetKnownHeader(HttpSysRequestHeader header) { return UnsafeNclNativeMethods.HttpApi.GetKnownHeader(_requestMemoryBlob.RequestBuffer, - _requestMemoryBlob.OriginalBlobAddress, (int)header); + _requestMemoryBlob.BufferAlignment, _requestMemoryBlob.OriginalBlobAddress, (int)header); } private void GetUnknownHeaders(IDictionary extra) { UnsafeNclNativeMethods.HttpApi.GetUnknownHeaders(extra, _requestMemoryBlob.RequestBuffer, - _requestMemoryBlob.OriginalBlobAddress); + _requestMemoryBlob.BufferAlignment, _requestMemoryBlob.OriginalBlobAddress); } void IDictionary.Add(string key, StringValues value) diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStream.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStream.cs index 059dfe6822..bfbbf8cb1c 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStream.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestStream.cs @@ -147,7 +147,9 @@ namespace Microsoft.Net.Http.Server if (_dataChunkIndex != -1) { - dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); + dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, + _requestContext.Request.BufferAlignment, _requestContext.Request.OriginalBlobAddress, + ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); } if (_dataChunkIndex == -1 && dataRead < size) @@ -223,7 +225,9 @@ namespace Microsoft.Net.Http.Server uint dataRead = 0; if (_dataChunkIndex != -1) { - dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); + dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.BufferAlignment, + _requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); + if (_dataChunkIndex != -1 && dataRead == size) { asyncResult = new RequestStreamAsyncResult(this, state, callback, buffer, offset, 0); @@ -339,7 +343,8 @@ namespace Microsoft.Net.Http.Server uint dataRead = 0; if (_dataChunkIndex != -1) { - dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); + dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.BufferAlignment, + _requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); if (_dataChunkIndex != -1 && dataRead == size) { UpdateAfterRead(UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS, dataRead); @@ -347,7 +352,7 @@ namespace Microsoft.Net.Http.Server return Task.FromResult((int)dataRead); } } - + if (_dataChunkIndex == -1 && dataRead < size) { uint statusCode = 0; diff --git a/src/Microsoft.Net.Http.Server/WebListener.cs b/src/Microsoft.Net.Http.Server/WebListener.cs index 942cc18c8f..23f48fcdde 100644 --- a/src/Microsoft.Net.Http.Server/WebListener.cs +++ b/src/Microsoft.Net.Http.Server/WebListener.cs @@ -890,7 +890,7 @@ namespace Microsoft.Net.Http.Server blob = new byte[size]; fixed (byte* blobPtr = blob) { - // Http.sys team: ServiceName will always be null if + // Http.sys team: ServiceName will always be null if // HTTP_RECEIVE_SECURE_CHANNEL_TOKEN flag is set. statusCode = UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate( RequestQueueHandle,