jellyfin-server/Emby.Common.Implementations/IO/SharpCifs/Util/Transport/Transport.cs

502 lines
16 KiB
C#
Raw Normal View History

2017-04-02 00:36:06 +00:00
// This code is derived from jcifs smb client library <jcifs at samba dot org>
// Ported by J. Arturo <webmaster at komodosoft dot net>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.IO;
using SharpCifs.Smb;
using SharpCifs.Util.Sharpen;
2017-06-21 06:46:57 +00:00
using System.Threading.Tasks;
2017-04-02 00:36:06 +00:00
namespace SharpCifs.Util.Transport
{
2017-06-21 06:46:57 +00:00
/// <summary>
/// This class simplifies communication for protocols that support
/// multiplexing requests.
/// </summary>
/// <remarks>
/// This class simplifies communication for protocols that support
/// multiplexing requests. It encapsulates a stream and some protocol
/// knowledge (provided by a concrete subclass) so that connecting,
/// disconnecting, sending, and receiving can be syncronized
/// properly. Apparatus is provided to send and receive requests
/// concurrently.
/// </remarks>
public abstract class Transport : IRunnable
{
internal static int Id;
//internal static LogStream log = LogStream.GetInstance();
public LogStream Log
{
get
{
return LogStream.GetInstance();
}
}
/// <exception cref="System.IO.IOException"></exception>
public static int Readn(InputStream @in, byte[] b, int off, int len)
{
int i = 0;
int n = -5;
while (i < len)
{
n = @in.Read(b, off + i, len - i);
if (n <= 0)
{
break;
}
i += n;
}
return i;
}
internal int State;
internal string Name = "Transport" + Id++;
internal Thread Thread;
internal TransportException Te;
protected internal Hashtable ResponseMap = new Hashtable();
/// <exception cref="System.IO.IOException"></exception>
protected internal abstract void MakeKey(ServerMessageBlock request);
/// <exception cref="System.IO.IOException"></exception>
protected internal abstract ServerMessageBlock PeekKey();
/// <exception cref="System.IO.IOException"></exception>
2017-04-02 00:36:06 +00:00
protected internal abstract void DoSend(ServerMessageBlock request);
2017-06-21 06:46:57 +00:00
/// <exception cref="System.IO.IOException"></exception>
protected internal abstract void DoRecv(Response response);
/// <exception cref="System.IO.IOException"></exception>
protected internal abstract void DoSkip();
/// <exception cref="System.IO.IOException"></exception>
public virtual void Sendrecv(ServerMessageBlock request, Response response, long timeout)
{
lock (this)
{
MakeKey(request);
response.IsReceived = false;
try
{
ResponseMap.Put(request, response);
DoSend(request);
response.Expiration = Runtime.CurrentTimeMillis() + timeout;
while (!response.IsReceived)
{
Runtime.Wait(this, timeout);
timeout = response.Expiration - Runtime.CurrentTimeMillis();
if (timeout <= 0)
{
throw new TransportException(
Name + " timedout waiting for response to " + request);
}
}
}
catch (IOException ioe)
{
if (Log.Level > 2)
{
Runtime.PrintStackTrace(ioe, Log);
}
try
{
Disconnect(true);
}
catch (IOException ioe2)
{
Runtime.PrintStackTrace(ioe2, Log);
}
throw;
}
catch (Exception ie)
{
throw new TransportException(ie);
}
finally
{
//Sharpen.Collections.Remove(response_map, request);
2017-04-02 00:36:06 +00:00
ResponseMap.Remove(request);
2017-06-21 06:46:57 +00:00
}
}
}
private void Loop()
{
while (Thread.CurrentThread().Equals(Thread))
{
if (Thread.IsCanceled)
break;
try
{
ServerMessageBlock key = PeekKey();
if (key == null)
{
throw new IOException("end of stream");
}
lock (this)
{
if (Thread.IsCanceled)
break;
Response response = (Response)ResponseMap.Get(key);
if (response == null)
{
if (Log.Level >= 4)
{
Log.WriteLine("Invalid key, skipping message");
}
DoSkip();
}
else
{
DoRecv(response);
if (Thread.IsCanceled)
break;
response.IsReceived = true;
Runtime.NotifyAll(this);
}
}
}
catch (Exception ex)
{
string msg = ex.Message;
bool timeout = msg != null && msg.Equals("Read timed out");
bool hard = timeout == false;
if (!timeout && Log.Level >= 3)
{
Runtime.PrintStackTrace(ex, Log);
}
try
{
Disconnect(hard);
}
catch (IOException ioe)
{
Runtime.PrintStackTrace(ioe, Log);
}
}
}
}
/// <exception cref="System.Exception"></exception>
protected internal abstract void DoConnect();
/// <exception cref="System.IO.IOException"></exception>
protected internal abstract void DoDisconnect(bool hard);
/// <exception cref="SharpCifs.Util.Transport.TransportException"></exception>
public virtual void Connect(long timeout)
{
lock (this)
{
try
{
switch (State)
{
case 0:
{
break;
}
case 3:
{
return;
}
case 4:
{
// already connected
State = 0;
throw new TransportException("Connection in error", Te);
}
default:
{
//TransportException te = new TransportException("Invalid state: " + state);
State = 0;
throw new TransportException("Invalid state: " + State);
}
}
State = 1;
Te = null;
if (Thread != null)
{
Thread.Cancel(true);
Thread.Dispose();
}
Thread = new Thread(this);
Thread.SetDaemon(true);
lock (Thread)
{
Thread.Start(true);
Runtime.Wait(Thread, timeout);
switch (State)
{
case 1:
{
State = 0;
Thread?.Cancel();
Thread?.Dispose();
Thread = null;
throw new TransportException("Connection timeout");
}
case 2:
{
if (Te != null)
{
State = 4;
Thread?.Cancel();
Thread?.Dispose();
Thread = null;
throw Te;
}
State = 3;
return;
}
}
}
}
catch (Exception ie)
{
State = 0;
Thread?.Cancel();
Thread?.Dispose();
Thread = null;
throw new TransportException(ie);
}
finally
{
if (State != 0 && State != 3 && State != 4)
{
if (Log.Level >= 1)
{
Log.WriteLine("Invalid state: " + State);
}
State = 0;
Thread?.Cancel();
Thread?.Dispose();
Thread = null;
}
}
}
}
/// <exception cref="System.IO.IOException"></exception>
public virtual void Disconnect(bool hard)
{
2017-04-02 00:36:06 +00:00
if (hard)
{
IOException ioe = null;
switch (State)
{
case 0:
{
return;
}
case 2:
{
hard = true;
goto case 3;
}
case 3:
{
if (ResponseMap.Count != 0 && !hard)
{
break;
}
try
{
DoDisconnect(hard);
}
catch (IOException ioe0)
{
ioe = ioe0;
}
goto case 4;
}
case 4:
{
2017-06-21 06:46:57 +00:00
Thread?.Cancel();
Thread?.Dispose();
2017-04-02 00:36:06 +00:00
Thread = null;
State = 0;
break;
}
default:
{
if (Log.Level >= 1)
{
Log.WriteLine("Invalid state: " + State);
}
2017-06-21 06:46:57 +00:00
Thread?.Cancel();
Thread?.Dispose();
2017-04-02 00:36:06 +00:00
Thread = null;
State = 0;
break;
}
}
if (ioe != null)
{
throw ioe;
}
return;
}
2017-06-21 06:46:57 +00:00
2017-04-02 00:36:06 +00:00
lock (this)
2017-06-21 06:46:57 +00:00
{
IOException ioe = null;
switch (State)
{
case 0:
{
return;
}
case 2:
{
hard = true;
goto case 3;
}
case 3:
{
if (ResponseMap.Count != 0 && !hard)
{
break;
}
try
{
DoDisconnect(hard);
}
catch (IOException ioe0)
{
ioe = ioe0;
}
goto case 4;
}
case 4:
{
Thread?.Cancel();
Thread?.Dispose();
Thread = null;
State = 0;
break;
}
default:
{
if (Log.Level >= 1)
{
Log.WriteLine("Invalid state: " + State);
}
Thread?.Cancel();
Thread?.Dispose();
Thread = null;
State = 0;
break;
}
}
if (ioe != null)
{
throw ioe;
}
}
}
public virtual void Run()
{
Thread runThread = Thread.CurrentThread();
if (runThread.IsCanceled)
return;
Exception ex0 = null;
try
{
DoConnect();
}
catch (Exception ex)
{
ex0 = ex;
// Defer to below where we're locked
return;
}
finally
{
lock (runThread)
{
if (!runThread.IsCanceled)
{
if (!runThread.Equals(Thread))
{
if (ex0 != null)
{
if (Log.Level >= 2)
{
Runtime.PrintStackTrace(ex0, Log);
}
}
//return;
}
if (ex0 != null)
{
Te = new TransportException(ex0);
}
State = 2;
// run connected
Runtime.Notify(runThread);
}
}
}
if (runThread.IsCanceled)
return;
Loop();
}
public override string ToString()
{
return Name;
}
}
2017-04-02 00:36:06 +00:00
}