private unsafe void SendError(ulong requestId, HttpStatusCode httpStatusCode, ArrayList challenges) { uint num2; byte[] buffer4; UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE pHttpResponse = new UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE { Version = new UnsafeNclNativeMethods.HttpApi.HTTP_VERSION() }; pHttpResponse.Version.MajorVersion = 1; pHttpResponse.Version.MinorVersion = 1; pHttpResponse.StatusCode = (ushort) httpStatusCode; string statusDescription = HttpListenerResponse.GetStatusDescription((int) httpStatusCode); uint pBytesSent = 0; byte[] bytes = Encoding.Default.GetBytes(statusDescription); if (((buffer4 = bytes) == null) || (buffer4.Length == 0)) { numRef = null; goto Label_006B; } fixed (byte* numRef = buffer4) { byte[] buffer5; Label_006B: pHttpResponse.pReason = (sbyte*) numRef; pHttpResponse.ReasonLength = (ushort) bytes.Length; byte[] buffer2 = Encoding.Default.GetBytes("0"); if (((buffer5 = buffer2) == null) || (buffer5.Length == 0)) { numRef2 = null; goto Label_00AF; } fixed (byte* numRef2 = buffer5) { Label_00AF: &pHttpResponse.Headers.KnownHeaders[11].pRawValue = (sbyte*) numRef2; &pHttpResponse.Headers.KnownHeaders[11].RawValueLength = (ushort) buffer2.Length; pHttpResponse.Headers.UnknownHeaderCount = (challenges == null) ? ((ushort) 0) : ((ushort) challenges.Count); GCHandle[] handleArray = null; UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[] http_unknown_headerArray = null; GCHandle handle = new GCHandle(); GCHandle handle2 = new GCHandle(); if (pHttpResponse.Headers.UnknownHeaderCount > 0) { handleArray = new GCHandle[pHttpResponse.Headers.UnknownHeaderCount]; http_unknown_headerArray = new UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[pHttpResponse.Headers.UnknownHeaderCount]; } try { if (pHttpResponse.Headers.UnknownHeaderCount > 0) { handle = GCHandle.Alloc(http_unknown_headerArray, GCHandleType.Pinned); pHttpResponse.Headers.pUnknownHeaders = (UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER*) Marshal.UnsafeAddrOfPinnedArrayElement(http_unknown_headerArray, 0); handle2 = GCHandle.Alloc(s_WwwAuthenticateBytes, GCHandleType.Pinned); sbyte* numPtr = (sbyte*) Marshal.UnsafeAddrOfPinnedArrayElement(s_WwwAuthenticateBytes, 0); for (int i = 0; i < handleArray.Length; i++) { byte[] buffer3 = Encoding.Default.GetBytes((string) challenges[i]); handleArray[i] = GCHandle.Alloc(buffer3, GCHandleType.Pinned); http_unknown_headerArray[i].pName = numPtr; http_unknown_headerArray[i].NameLength = (ushort) s_WwwAuthenticateBytes.Length; http_unknown_headerArray[i].pRawValue = (sbyte*) Marshal.UnsafeAddrOfPinnedArrayElement(buffer3, 0); http_unknown_headerArray[i].RawValueLength = (ushort) buffer3.Length; } } num2 = UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse(this.m_RequestQueueHandle, requestId, 0, &pHttpResponse, null, &pBytesSent, SafeLocalFree.Zero, 0, null, null); } finally { if (handle.IsAllocated) { handle.Free(); } if (handle2.IsAllocated) { handle2.Free(); } if (handleArray != null) { for (int j = 0; j < handleArray.Length; j++) { if (handleArray[j].IsAllocated) { handleArray[j].Free(); } } } } } } if (num2 != 0) { HttpListenerContext.CancelRequest(this.m_RequestQueueHandle, requestId); } }
private List<GCHandle> SerializeHeaders(ref UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADERS headers, bool isWebSocketHandshake) { UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[] unknownHeaders = null; List<GCHandle> pinnedHeaders; GCHandle gcHandle; /* // here we would check for BoundaryType.Raw, in this case we wouldn't need to do anything if (m_BoundaryType==BoundaryType.Raw) { return null; } */ GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(HTTP_RESPONSE_HEADERS)"); if (Headers.Count==0) { return null; } string headerName; string headerValue; int lookup; byte[] bytes = null; pinnedHeaders = new List<GCHandle>(); //--------------------------------------------------- // DTS Issue: 609383: // The Set-Cookie headers are being merged into one. // There are two issues here. // 1. When Set-Cookie headers are set through SetCookie method on the ListenerResponse, // there is code in the SetCookie method and the methods it calls to flatten the Set-Cookie // values. This blindly concatenates the cookies with a comma delimiter. There could be // a cookie value that contains comma, but we don't escape it with %XX value // // As an alternative users can add the Set-Cookie header through the AddHeader method // like ListenerResponse.Headers.Add("name", "value") // That way they can add multiple headers - AND They can format the value like they want it. // // 2. Now that the header collection contains multiple Set-Cookie name, value pairs // you would think the problem would go away. However here is an interesting thing. // For NameValueCollection, when you add // "Set-Cookie", "value1" // "Set-Cookie", "value2" // The NameValueCollection.Count == 1. Because there is only one key // NameValueCollection.Get("Set-Cookie") would conviniently take these two valuess // concatenate them with a comma like // value1,value2. // In order to get individual values, you need to use // string[] values = NameValueCollection.GetValues("Set-Cookie"); // // ------------------------------------------------------------- // So here is the proposed fix here. // We must first to loop through all the NameValueCollection keys // and if the name is a unknown header, we must compute the number of // values it has. Then, we should allocate that many unknown header array // elements. // // Note that a part of the fix here is to treat Set-Cookie as an unknown header // // //----------------------------------------------------------- int numUnknownHeaders = 0; for (int index=0; index<Headers.Count; index++) { headerName = Headers.GetKey(index) as string; //See if this is an unknown header lookup = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName); //Treat Set-Cookie as well as Connection header in Websocket mode as unknown if (lookup == (int)HttpResponseHeader.SetCookie || isWebSocketHandshake && lookup == (int)HttpResponseHeader.Connection) { lookup = -1; } if(lookup == -1) { string[] headerValues = Headers.GetValues(index); numUnknownHeaders += headerValues.Length; } } try{ fixed (UnsafeNclNativeMethods.HttpApi.HTTP_KNOWN_HEADER* pKnownHeaders = &headers.KnownHeaders) { for (int index=0; index<Headers.Count; index++) { headerName = Headers.GetKey(index) as string; headerValue = Headers.Get(index) as string; lookup = UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName); if (lookup == (int)HttpResponseHeader.SetCookie || isWebSocketHandshake && lookup == (int)HttpResponseHeader.Connection) { lookup = -1; } GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(" + index + "/" + Headers.Count + ") headerName:" + ValidationHelper.ToString(headerName) + " lookup:" + lookup + " headerValue:" + ValidationHelper.ToString(headerValue)); if (lookup==-1) { if (unknownHeaders==null) { //---------------------------------------- //*** This following comment is no longer true *** // we waste some memory here (up to 32*41=1312 bytes) but we gain speed //unknownHeaders = new UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[Headers.Count-index]; //-------------------------------------------- unknownHeaders = new UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[numUnknownHeaders]; gcHandle = GCHandle.Alloc(unknownHeaders, GCHandleType.Pinned); pinnedHeaders.Add(gcHandle); headers.pUnknownHeaders = (UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER*)gcHandle.AddrOfPinnedObject(); } //---------------------------------------- //FOR UNKNOWN HEADERS //ALLOW MULTIPLE HEADERS to be added //--------------------------------------- string[] headerValues = Headers.GetValues(index); for(int headerValueIndex = 0; headerValueIndex < headerValues.Length; headerValueIndex++) { //Add Name bytes = new byte[WebHeaderCollection.HeaderEncoding.GetByteCount(headerName)]; unknownHeaders[headers.UnknownHeaderCount].NameLength = (ushort)bytes.Length; WebHeaderCollection.HeaderEncoding.GetBytes(headerName, 0, bytes.Length, bytes, 0); gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); pinnedHeaders.Add(gcHandle); unknownHeaders[headers.UnknownHeaderCount].pName = (sbyte*)gcHandle.AddrOfPinnedObject(); //Add Value headerValue = headerValues[headerValueIndex]; bytes = new byte[WebHeaderCollection.HeaderEncoding.GetByteCount(headerValue)]; unknownHeaders[headers.UnknownHeaderCount].RawValueLength = (ushort)bytes.Length; WebHeaderCollection.HeaderEncoding.GetBytes(headerValue, 0, bytes.Length, bytes, 0); gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); pinnedHeaders.Add(gcHandle); unknownHeaders[headers.UnknownHeaderCount].pRawValue = (sbyte*)gcHandle.AddrOfPinnedObject(); headers.UnknownHeaderCount++; GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(Unknown) UnknownHeaderCount:" + headers.UnknownHeaderCount); } } else { GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(Known) HttpResponseHeader[" + lookup + "]:" + ((HttpResponseHeader)lookup) + " headerValue:" + ValidationHelper.ToString(headerValue)); if (headerValue!=null) { bytes = new byte[WebHeaderCollection.HeaderEncoding.GetByteCount(headerValue)]; pKnownHeaders[lookup].RawValueLength = (ushort)bytes.Length; WebHeaderCollection.HeaderEncoding.GetBytes(headerValue, 0, bytes.Length, bytes, 0); gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); pinnedHeaders.Add(gcHandle); pKnownHeaders[lookup].pRawValue = (sbyte*)gcHandle.AddrOfPinnedObject(); GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SerializeHeaders(Known) pRawValue:" + ValidationHelper.ToString((IntPtr)(pKnownHeaders[lookup].pRawValue)) + " RawValueLength:" + pKnownHeaders[lookup].RawValueLength + " lookup:" + lookup); GlobalLog.Dump((IntPtr)pKnownHeaders[lookup].pRawValue, 0, pKnownHeaders[lookup].RawValueLength); } } } } } catch { FreePinnedHeaders(pinnedHeaders); throw; } return pinnedHeaders; }
private void SendError(ulong requestId, HttpStatusCode httpStatusCode, ArrayList challenges) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::SendInternalError() requestId:" + ValidationHelper.ToString(requestId)); UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE httpResponse = new UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE(); httpResponse.Version = new UnsafeNclNativeMethods.HttpApi.HTTP_VERSION(); httpResponse.Version.MajorVersion = (ushort)1; httpResponse.Version.MinorVersion = (ushort)1; httpResponse.StatusCode = (ushort)httpStatusCode; string statusDescription = HttpStatusDescription.Get(httpStatusCode); uint DataWritten = 0; uint statusCode; byte[] byteReason = Encoding.Default.GetBytes(statusDescription); fixed (byte* pReason = byteReason) { httpResponse.pReason = (sbyte*)pReason; httpResponse.ReasonLength = (ushort)byteReason.Length; byte[] byteContentLength = Encoding.Default.GetBytes("0"); fixed (byte* pContentLength = byteContentLength) { (&httpResponse.Headers.KnownHeaders)[(int)HttpResponseHeader.ContentLength].pRawValue = (sbyte*)pContentLength; (&httpResponse.Headers.KnownHeaders)[(int)HttpResponseHeader.ContentLength].RawValueLength = (ushort)byteContentLength.Length; httpResponse.Headers.UnknownHeaderCount = checked((ushort) (challenges == null ? 0 : challenges.Count)); GCHandle[] challengeHandles = null; UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[] headersArray = null; GCHandle headersArrayHandle = new GCHandle(); GCHandle wwwAuthenticateHandle = new GCHandle(); if (httpResponse.Headers.UnknownHeaderCount > 0) { challengeHandles = new GCHandle[httpResponse.Headers.UnknownHeaderCount]; headersArray = new UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[httpResponse.Headers.UnknownHeaderCount]; } try { if (httpResponse.Headers.UnknownHeaderCount > 0) { headersArrayHandle = GCHandle.Alloc(headersArray, GCHandleType.Pinned); httpResponse.Headers.pUnknownHeaders = (UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER*) Marshal.UnsafeAddrOfPinnedArrayElement(headersArray, 0); wwwAuthenticateHandle = GCHandle.Alloc(s_WwwAuthenticateBytes, GCHandleType.Pinned); sbyte* wwwAuthenticate = (sbyte*) Marshal.UnsafeAddrOfPinnedArrayElement(s_WwwAuthenticateBytes, 0); for (int i = 0; i < challengeHandles.Length; i++) { byte[] byteChallenge = Encoding.Default.GetBytes((string) challenges[i]); challengeHandles[i] = GCHandle.Alloc(byteChallenge, GCHandleType.Pinned); headersArray[i].pName = wwwAuthenticate; headersArray[i].NameLength = (ushort) s_WwwAuthenticateBytes.Length; headersArray[i].pRawValue = (sbyte*) Marshal.UnsafeAddrOfPinnedArrayElement(byteChallenge, 0); headersArray[i].RawValueLength = checked((ushort) byteChallenge.Length); } } GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::SendInternalError() calling UnsafeNclNativeMethods.HttpApi.HttpSendHtthttpResponse"); statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse( m_RequestQueueHandle, requestId, 0, &httpResponse, null, &DataWritten, SafeLocalFree.Zero, 0, null, null ); } finally { if (headersArrayHandle.IsAllocated) { headersArrayHandle.Free(); } if (wwwAuthenticateHandle.IsAllocated) { wwwAuthenticateHandle.Free(); } if (challengeHandles != null) { for (int i = 0; i < challengeHandles.Length; i++) { if (challengeHandles[i].IsAllocated) { challengeHandles[i].Free(); } } } } } } GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::SendInternalError() call to UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse returned:" + statusCode); if (statusCode!=UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { // if we fail to send a 401 something's seriously wrong, abort the request GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() SendUnauthorized() returned:" + statusCode); HttpListenerContext.CancelRequest(m_RequestQueueHandle, requestId); } }