/*
* 12.3
* HttpSendHttpResponse() and HttpSendResponseEntityBody() Flag Values.
* The following flags can be used on calls to HttpSendHttpResponse() and HttpSendResponseEntityBody() API calls:
*
#define HTTP_SEND_RESPONSE_FLAG_DISCONNECT 0x00000001
#define HTTP_SEND_RESPONSE_FLAG_MORE_DATA 0x00000002
#define HTTP_SEND_RESPONSE_FLAG_RAW_HEADER 0x00000004
#define HTTP_SEND_RESPONSE_FLAG_VALID 0x00000007
*
* HTTP_SEND_RESPONSE_FLAG_DISCONNECT:
* specifies that the network connection should be disconnected immediately after
* sending the response, overriding the HTTP protocol's persistent connection features.
* HTTP_SEND_RESPONSE_FLAG_MORE_DATA:
* specifies that additional entity body data will be sent by the caller. Thus,
* the last call HttpSendResponseEntityBody for a RequestId, will have this flag reset.
* HTTP_SEND_RESPONSE_RAW_HEADER:
* specifies that a caller of HttpSendResponseEntityBody() is intentionally omitting
* a call to HttpSendHttpResponse() in order to bypass normal header processing. The
* actual HTTP header will be generated by the application and sent as entity body.
* This flag should be passed on the first call to HttpSendResponseEntityBody, and
* not after. Thus, flag is not applicable to HttpSendHttpResponse.
*/
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);
}