|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//
// System.Net.ListenerAsyncResult
//
// Authors:
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// Copyright (c) 2005 Ximian, Inc (http://www.ximian.com)
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Runtime.ExceptionServices;
using System.Threading;
namespace System.Net
{
internal sealed class ListenerAsyncResult : IAsyncResult
{
private ManualResetEvent? _handle;
private bool _synch;
private bool _completed;
private readonly AsyncCallback? _cb;
private readonly object? _state;
private Exception? _exception;
private HttpListenerContext? _context;
private readonly object _locker = new object();
private ListenerAsyncResult? _forward;
internal readonly HttpListener _parent;
internal bool _endCalled;
internal bool _inGet;
public ListenerAsyncResult(HttpListener parent, AsyncCallback? cb, object? state)
{
_parent = parent;
_cb = cb;
_state = state;
}
internal void Complete(Exception exc)
{
if (_forward != null)
{
_forward.Complete(exc);
return;
}
_exception = exc;
if (_inGet && (exc is ObjectDisposedException))
_exception = new HttpListenerException((int)HttpStatusCode.InternalServerError, SR.net_listener_close);
lock (_locker)
{
_completed = true;
_handle?.Set();
if (_cb != null)
ThreadPool.UnsafeQueueUserWorkItem(s_invokeCB, this);
}
}
private static readonly WaitCallback s_invokeCB = InvokeCallback;
private static void InvokeCallback(object? o)
{
ListenerAsyncResult ares = (ListenerAsyncResult)o!;
if (ares._forward != null)
{
InvokeCallback(ares._forward);
return;
}
try
{
ares._cb!(ares);
}
catch
{
}
}
internal void Complete(HttpListenerContext context)
{
Complete(context, false);
}
internal void Complete(HttpListenerContext context, bool synch)
{
if (_forward != null)
{
_forward.Complete(context, synch);
return;
}
_synch = synch;
_context = context;
lock (_locker)
{
bool authFailure = false;
try
{
context.AuthenticationSchemes = context._listener!.SelectAuthenticationScheme(context);
}
catch (OutOfMemoryException oom)
{
context.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
_exception = oom;
}
catch
{
authFailure = true;
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
if (context.AuthenticationSchemes != AuthenticationSchemes.None &&
(context.AuthenticationSchemes & AuthenticationSchemes.Anonymous) != AuthenticationSchemes.Anonymous &&
(context.AuthenticationSchemes & AuthenticationSchemes.Basic) != AuthenticationSchemes.Basic)
{
authFailure = true;
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else if (context.AuthenticationSchemes == AuthenticationSchemes.Basic)
{
HttpStatusCode errorCode = HttpStatusCode.Unauthorized;
string? authHeader = context.Request.Headers["Authorization"];
if (authHeader == null ||
!HttpListenerContext.IsBasicHeader(authHeader) ||
authHeader.Length < AuthenticationTypes.Basic.Length + 2 ||
!HttpListenerContext.TryParseBasicAuth(authHeader.Substring(AuthenticationTypes.Basic.Length + 1), out errorCode, out _, out _))
{
authFailure = true;
context.Response.StatusCode = (int)errorCode;
if (errorCode == HttpStatusCode.Unauthorized)
{
context.Response.Headers["WWW-Authenticate"] = context.AuthenticationSchemes + " realm=\"" + context._listener!.Realm + "\"";
}
}
}
if (authFailure)
{
context.Response.OutputStream.Close();
IAsyncResult ares = context._listener!.BeginGetContext(_cb, _state);
_forward = (ListenerAsyncResult)ares;
lock (_forward._locker)
{
if (_handle != null)
_forward._handle = _handle;
}
ListenerAsyncResult next = _forward;
for (int i = 0; next._forward != null; i++)
{
if (i > 20)
Complete(new HttpListenerException((int)HttpStatusCode.Unauthorized, SR.net_listener_auth_errors));
next = next._forward;
}
}
else
{
_completed = true;
_synch = false;
_handle?.Set();
if (_cb != null)
ThreadPool.UnsafeQueueUserWorkItem(s_invokeCB, this);
}
}
}
internal HttpListenerContext? GetContext()
{
if (_forward != null)
{
return _forward.GetContext();
}
if (_exception != null)
{
ExceptionDispatchInfo.Throw(_exception);
}
return _context;
}
public object? AsyncState
{
get
{
if (_forward != null)
return _forward.AsyncState;
return _state;
}
}
public WaitHandle AsyncWaitHandle
{
get
{
if (_forward != null)
return _forward.AsyncWaitHandle;
lock (_locker)
{
_handle ??= new ManualResetEvent(_completed);
}
return _handle;
}
}
public bool CompletedSynchronously
{
get
{
if (_forward != null)
return _forward.CompletedSynchronously;
return _synch;
}
}
public bool IsCompleted
{
get
{
if (_forward != null)
return _forward.IsCompleted;
lock (_locker)
{
return _completed;
}
}
}
}
}
|