265 lines
7.0 KiB
C#
Raw Normal View History

#if SECURITY_DEP
//
// AsyncProtocolRequest.cs
//
// Author:
// Martin Baulig <martin.baulig@xamarin.com>
//
// Copyright (c) 2015 Xamarin, Inc.
//
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using SD = System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Mono.Net.Security
{
delegate AsyncOperationStatus AsyncOperation (AsyncProtocolRequest asyncRequest, AsyncOperationStatus status);
class BufferOffsetSize
{
public byte[] Buffer;
public int Offset;
public int Size;
public int TotalBytes;
public bool Complete;
public int EndOffset {
get { return Offset + Size; }
}
public int Remaining {
get { return Buffer.Length - Offset - Size; }
}
public BufferOffsetSize (byte[] buffer, int offset, int size)
{
Buffer = buffer;
Offset = offset;
Size = size;
Complete = false;
}
public override string ToString ()
{
return string.Format ("[BufferOffsetSize: {0} {1}]", Offset, Size);
}
}
class BufferOffsetSize2 : BufferOffsetSize
{
public readonly int InitialSize;
public BufferOffsetSize2 (int size)
: base (new byte [size], 0, 0)
{
InitialSize = size;
}
public void Reset ()
{
Offset = Size = 0;
TotalBytes = 0;
Buffer = new byte [InitialSize];
Complete = false;
}
public void MakeRoom (int size)
{
if (Remaining >= size)
return;
int missing = size - Remaining;
if (Offset == 0 && Size == 0) {
Buffer = new byte [size];
return;
}
var buffer = new byte [Buffer.Length + missing];
Buffer.CopyTo (buffer, 0);
Buffer = buffer;
}
public void AppendData (byte[] buffer, int offset, int size)
{
MakeRoom (size);
System.Buffer.BlockCopy (buffer, offset, Buffer, EndOffset, size);
Size += size;
}
}
enum AsyncOperationStatus {
NotStarted,
Initialize,
Continue,
Running,
Complete,
WantRead,
WantWrite,
ReadDone
}
class AsyncProtocolRequest
{
public readonly MobileAuthenticatedStream Parent;
public readonly BufferOffsetSize UserBuffer;
int RequestedSize;
public int CurrentSize;
public int UserResult;
AsyncOperation Operation;
int Status;
public readonly int ID = ++next_id;
static int next_id;
public readonly LazyAsyncResult UserAsyncResult;
public AsyncProtocolRequest (MobileAuthenticatedStream parent, LazyAsyncResult lazyResult, BufferOffsetSize userBuffer = null)
{
Parent = parent;
UserAsyncResult = lazyResult;
UserBuffer = userBuffer;
}
public bool CompleteWithError (Exception ex)
{
Status = (int)AsyncOperationStatus.Complete;
if (UserAsyncResult == null)
return true;
if (!UserAsyncResult.InternalPeekCompleted)
UserAsyncResult.InvokeCallback (ex);
return false;
}
[SD.Conditional ("MARTIN_DEBUG")]
protected void Debug (string message, params object[] args)
{
Parent.Debug ("AsyncProtocolRequest({0}:{1}): {2}", Parent.ID, ID, string.Format (message, args));
}
internal void RequestRead (int size)
{
var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.WantRead, (int)AsyncOperationStatus.Running);
Debug ("RequestRead: {0} {1}", oldStatus, size);
if (oldStatus == AsyncOperationStatus.Running)
RequestedSize = size;
else if (oldStatus == AsyncOperationStatus.WantRead)
RequestedSize += size;
else if (oldStatus != AsyncOperationStatus.WantWrite)
throw new InvalidOperationException ();
}
internal void ResetRead ()
{
var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Complete, (int)AsyncOperationStatus.WantRead);
Debug ("ResetRead: {0} {1}", oldStatus, Status);
}
internal void RequestWrite ()
{
var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.WantWrite, (int)AsyncOperationStatus.Running);
if (oldStatus == AsyncOperationStatus.Running)
return;
else if (oldStatus != AsyncOperationStatus.WantRead && oldStatus != AsyncOperationStatus.WantWrite)
throw new InvalidOperationException ();
}
internal void StartOperation (AsyncOperation operation)
{
Debug ("Start Operation: {0} {1}", Status, operation);
if (Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Initialize, (int)AsyncOperationStatus.NotStarted) != (int)AsyncOperationStatus.NotStarted)
throw new InvalidOperationException ();
Operation = operation;
if (UserAsyncResult == null) {
StartOperation ();
return;
}
ThreadPool.QueueUserWorkItem (_ => StartOperation ());
}
void StartOperation ()
{
try {
ProcessOperation ();
if (UserAsyncResult != null && !UserAsyncResult.InternalPeekCompleted)
UserAsyncResult.InvokeCallback (UserResult);
} catch (Exception ex) {
if (UserAsyncResult == null)
throw;
if (!UserAsyncResult.InternalPeekCompleted)
UserAsyncResult.InvokeCallback (ex);
}
}
void ProcessOperation ()
{
AsyncOperationStatus status;
do {
status = (AsyncOperationStatus)Interlocked.Exchange (ref Status, (int)AsyncOperationStatus.Running);
Debug ("ProcessOperation: {0}", status);
status = ProcessOperation (status);
var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)status, (int)AsyncOperationStatus.Running);
Debug ("ProcessOperation done: {0} -> {1}", oldStatus, status);
if (oldStatus != AsyncOperationStatus.Running) {
if (status == oldStatus || status == AsyncOperationStatus.Continue || status == AsyncOperationStatus.Complete)
status = oldStatus;
else
throw new InvalidOperationException ();
}
} while (status != AsyncOperationStatus.Complete);
}
AsyncOperationStatus ProcessOperation (AsyncOperationStatus status)
{
if (status == AsyncOperationStatus.WantRead) {
if (RequestedSize < 0)
throw new InvalidOperationException ();
else if (RequestedSize == 0)
return AsyncOperationStatus.Continue;
Debug ("ProcessOperation - read inner: {0}", RequestedSize);
var ret = Parent.InnerRead (RequestedSize);
Debug ("ProcessOperation - read inner done: {0} - {1}", RequestedSize, ret);
if (ret < 0)
return AsyncOperationStatus.ReadDone;
RequestedSize -= ret;
if (ret == 0 || RequestedSize == 0)
return AsyncOperationStatus.Continue;
else
return AsyncOperationStatus.WantRead;
} else if (status == AsyncOperationStatus.WantWrite) {
Parent.InnerWrite ();
return AsyncOperationStatus.Continue;
} else if (status == AsyncOperationStatus.Initialize || status == AsyncOperationStatus.Continue) {
Debug ("ProcessOperation - continue");
status = Operation (this, status);
Debug ("ProcessOperation - continue done: {0}", status);
return status;
} else if (status == AsyncOperationStatus.ReadDone) {
Debug ("ProcessOperation - read done");
status = Operation (this, status);
Debug ("ProcessOperation - read done: {0}", status);
return status;
}
throw new InvalidOperationException ();
}
}
}
#endif