internal unsafe uint SendHeaders(Interop.HttpApi.HTTP_DATA_CHUNK* pDataChunk,
HttpResponseStreamAsyncResult asyncResult,
Interop.HttpApi.HTTP_FLAGS flags,
bool isWebSocketHandshake)
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"pDataChunk: { ((IntPtr)pDataChunk)}, asyncResult: {asyncResult}");
Debug.Assert(!SentHeaders, "SentHeaders is true.");
if (StatusCode == (int)HttpStatusCode.Unauthorized)
{
// User set 401
// Using the configured Auth schemes, populate the auth challenge headers. This is for scenarios where
// Anonymous access is allowed for some resources, but the server later determines that authorization
// is required for this request.
HttpListenerContext.SetAuthenticationHeaders();
}
// Log headers
if (NetEventSource.IsEnabled)
{
StringBuilder sb = new StringBuilder("HttpListenerResponse Headers:\n");
for (int i = 0; i < Headers.Count; i++)
{
sb.Append("\t");
sb.Append(Headers.GetKey(i));
sb.Append(" : ");
sb.Append(Headers.Get(i));
sb.Append("\n");
}
if (NetEventSource.IsEnabled) NetEventSource.Info(this, sb.ToString());
}
_responseState = ResponseState.SentHeaders;
uint statusCode;
uint bytesSent;
List<GCHandle> pinnedHeaders = SerializeHeaders(ref _nativeResponse.Headers, isWebSocketHandshake);
try
{
if (pDataChunk != null)
{
_nativeResponse.EntityChunkCount = 1;
_nativeResponse.pEntityChunks = pDataChunk;
}
else if (asyncResult != null && asyncResult.pDataChunks != null)
{
_nativeResponse.EntityChunkCount = asyncResult.dataChunkCount;
_nativeResponse.pEntityChunks = asyncResult.pDataChunks;
}
else
{
_nativeResponse.EntityChunkCount = 0;
_nativeResponse.pEntityChunks = null;
}
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendHttpResponse flags:" + flags);
if (StatusDescription.Length > 0)
{
byte[] statusDescriptionBytes = new byte[WebHeaderEncoding.GetByteCount(StatusDescription)];
fixed (byte* pStatusDescription = statusDescriptionBytes)
{
_nativeResponse.ReasonLength = (ushort)statusDescriptionBytes.Length;
WebHeaderEncoding.GetBytes(StatusDescription, 0, statusDescriptionBytes.Length, statusDescriptionBytes, 0);
_nativeResponse.pReason = (sbyte*)pStatusDescription;
fixed (Interop.HttpApi.HTTP_RESPONSE* pResponse = &_nativeResponse)
{
statusCode =
Interop.HttpApi.HttpSendHttpResponse(
HttpListenerContext.RequestQueueHandle,
HttpListenerRequest.RequestId,
(uint)flags,
pResponse,
null,
&bytesSent,
SafeLocalAllocHandle.Zero,
0,
asyncResult == null ? null : asyncResult._pOverlapped,
null);
if (asyncResult != null &&
statusCode == Interop.HttpApi.ERROR_SUCCESS &&
HttpListener.SkipIOCPCallbackOnSuccess)
{
asyncResult.IOCompleted(statusCode, bytesSent);
// IO operation completed synchronously - callback won't be called to signal completion.
}
}
}
}
else
{
fixed (Interop.HttpApi.HTTP_RESPONSE* pResponse = &_nativeResponse)
{
statusCode =
Interop.HttpApi.HttpSendHttpResponse(
HttpListenerContext.RequestQueueHandle,
HttpListenerRequest.RequestId,
(uint)flags,
pResponse,
null,
&bytesSent,
SafeLocalAllocHandle.Zero,
0,
asyncResult == null ? null : asyncResult._pOverlapped,
null);
if (asyncResult != null &&
statusCode == Interop.HttpApi.ERROR_SUCCESS &&
HttpListener.SkipIOCPCallbackOnSuccess)
{
asyncResult.IOCompleted(statusCode, bytesSent);
// IO operation completed synchronously - callback won't be called to signal completion.
}
}
}
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendHttpResponse returned:" + statusCode);
}
finally
{
FreePinnedHeaders(pinnedHeaders);
}
return statusCode;
}