Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/rx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Rx.NET/System.Reactive.Runtime.Remoting/Reactive/Linq/QueryLanguage.Remoting.cs')
-rw-r--r--Rx.NET/System.Reactive.Runtime.Remoting/Reactive/Linq/QueryLanguage.Remoting.cs255
1 files changed, 255 insertions, 0 deletions
diff --git a/Rx.NET/System.Reactive.Runtime.Remoting/Reactive/Linq/QueryLanguage.Remoting.cs b/Rx.NET/System.Reactive.Runtime.Remoting/Reactive/Linq/QueryLanguage.Remoting.cs
new file mode 100644
index 0000000..9d85a41
--- /dev/null
+++ b/Rx.NET/System.Reactive.Runtime.Remoting/Reactive/Linq/QueryLanguage.Remoting.cs
@@ -0,0 +1,255 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+#if !NO_REMOTING
+using System.Reactive.Disposables;
+using System.Runtime.Remoting;
+using System.Runtime.Remoting.Lifetime;
+using System.Security;
+using System.Threading;
+
+//
+// DESIGN: The MarshalByRefObject (MBRO) implementations for RemotableObserver and RemotableSubscription act as
+// self-sponsoring objects controlling their lease times in order to tie those to the lifetime of the
+// underlying observable sequence (ended by OnError or OnCompleted) or the user-controlled subscription
+// lifetime. If we were to implement InitializeLifetimeService to return null, we'd end up with leases
+// that are infinite, so we need a more fine-grained lease scheme. The default configuration would time
+// out after 5 minutes, causing clients to fail while they're still observing the sequence. To solve
+// this, those MBROs also implement ISponsor with a Renewal method that continues to renew the lease
+// upon every call. When the sequence comes to an end or the subscription is disposed, the sponsor gets
+// unregistered, allowing the objects to be reclaimed eventually by the Remoting infrastructure.
+//
+// SECURITY: Registration and unregistration of sponsors is protected by SecurityCritical annotations. The
+// implementation of ISponsor is known (i.e. no foreign implementation can be passed in) at the call
+// sites of the Register and Unregister methods. The call to Register happens in the SecurityCritical
+// InitializeLifetimeService method and is called by trusted Remoting infrastructure. The Renewal
+// method is also marked as SecurityCritical and called by Remoting. The Unregister method is wrapped
+// in a ***SecurityTreatAsSafe*** private method which only gets called by the observer's OnError and
+// OnCompleted notifications, or the subscription's Dispose method. In the former case, the sequence
+// indicates it has reached the end, and hence resources can be reclaimed. Clients will no longer be
+// connected to the source due to auto-detach behavior enforced in the SerializableObservable client-
+// side implementation. In the latter case of disposing the subscription, the client is in control
+// and will cause the underlying remote subscription to be disposed as well, allowing resources to be
+// reclaimed. Rogue messages on either the data or the subscription channel can cause a DoS of the
+// client-server communication but this is subject to the security of the Remoting channels used. In
+// no case an untrusted party can cause _extension_ of the lease time.
+//
+//
+// Notice this assembly is marked as APTCA in official builds, causing methods to be treated as transparent,
+// thus requiring the ***SecurityTreatAsSafe*** annotation on the security boundaries described above. When not
+// applied, the following exception would occur at runtime:
+//
+// System.MethodAccessException:
+//
+// Attempt by security transparent method 'System.Reactive.Linq.QueryLanguage+RemotableObservable`1+
+// RemotableSubscription<T>.Unregister()' to access security critical method 'System.Runtime.Remoting.Lifetime.
+// ILease.Unregister(System.Runtime.Remoting.Lifetime.ISponsor)' failed.
+//
+// Assembly 'System.Reactive.Linq, Version=2.0.ymmdd.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
+// is marked with the AllowPartiallyTrustedCallersAttribute, and uses the level 2 security transparency model.
+// Level 2 transparency causes all methods in AllowPartiallyTrustedCallers assemblies to become security
+// transparent by default, which may be the cause of this exception.
+//
+//
+// The two CodeAnalysis suppressions below are explained by the Justification property (scroll to the right):
+//
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope = "member", Target = "System.Reactive.Linq.QueryLanguage+RemotableObserver`1.#Unregister()", Justification = "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope = "member", Target = "System.Reactive.Linq.QueryLanguage+RemotableObservable`1+RemotableSubscription.#Unregister()", Justification = "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")]
+
+namespace System.Reactive.Linq
+{
+ public static partial class RemotingObservable
+ {
+ #region Remotable
+
+ private static IObservable<TSource> Remotable_<TSource>(IObservable<TSource> source)
+ {
+ return new SerializableObservable<TSource>(new RemotableObservable<TSource>(source, null));
+ }
+
+ private static IObservable<TSource> Remotable_<TSource>(IObservable<TSource> source, ILease lease)
+ {
+ return new SerializableObservable<TSource>(new RemotableObservable<TSource>(source, lease));
+ }
+
+ [Serializable]
+ class SerializableObservable<T> : IObservable<T>
+ {
+ readonly RemotableObservable<T> remotableObservable;
+
+ public SerializableObservable(RemotableObservable<T> remotableObservable)
+ {
+ this.remotableObservable = remotableObservable;
+ }
+
+ public IDisposable Subscribe(IObserver<T> observer)
+ {
+ var d = new SingleAssignmentDisposable();
+
+ var o = Observer.Create<T>(
+ observer.OnNext,
+ ex =>
+ {
+ //
+ // Make call to the remote subscription, causing lease renewal to be stopped.
+ //
+ using (d)
+ {
+ observer.OnError(ex);
+ }
+ },
+ () =>
+ {
+ //
+ // Make call to the remote subscription, causing lease renewal to be stopped.
+ //
+ using (d)
+ {
+ observer.OnCompleted();
+ }
+ }
+ );
+
+ //
+ // [OK] Use of unsafe Subscribe: non-pretentious transparent wrapping through remoting; exception coming from the remote object is not re-routed.
+ //
+ d.Disposable = remotableObservable.Subscribe/*Unsafe*/(new RemotableObserver<T>(o));
+
+ return d;
+ }
+ }
+
+ class RemotableObserver<T> : MarshalByRefObject, IObserver<T>, ISponsor
+ {
+ readonly IObserver<T> underlyingObserver;
+
+ public RemotableObserver(IObserver<T> underlyingObserver)
+ {
+ this.underlyingObserver = underlyingObserver;
+ }
+
+ public void OnNext(T value)
+ {
+ underlyingObserver.OnNext(value);
+ }
+
+ public void OnError(Exception exception)
+ {
+ try
+ {
+ underlyingObserver.OnError(exception);
+ }
+ finally
+ {
+ Unregister();
+ }
+ }
+
+ public void OnCompleted()
+ {
+ try
+ {
+ underlyingObserver.OnCompleted();
+ }
+ finally
+ {
+ Unregister();
+ }
+ }
+
+ [SecuritySafeCritical] // See remarks at the top of the file.
+ private void Unregister()
+ {
+ var lease = (ILease)RemotingServices.GetLifetimeService(this);
+ if (lease != null)
+ lease.Unregister(this);
+ }
+
+ [SecurityCritical]
+ public override object InitializeLifetimeService()
+ {
+ var lease = (ILease)base.InitializeLifetimeService();
+ lease.Register(this);
+ return lease;
+ }
+
+ [SecurityCritical]
+ TimeSpan ISponsor.Renewal(ILease lease)
+ {
+ return lease.InitialLeaseTime;
+ }
+ }
+
+ [Serializable]
+ sealed class RemotableObservable<T> : MarshalByRefObject, IObservable<T>
+ {
+ readonly IObservable<T> underlyingObservable;
+ readonly ILease lease;
+
+ public RemotableObservable(IObservable<T> underlyingObservable, ILease lease)
+ {
+ this.underlyingObservable = underlyingObservable;
+ this.lease = lease;
+ }
+
+ public IDisposable Subscribe(IObserver<T> observer)
+ {
+ //
+ // [OK] Use of unsafe Subscribe: non-pretentious transparent wrapping through remoting; throwing across remoting boundaries is fine.
+ //
+ return new RemotableSubscription(underlyingObservable.Subscribe/*Unsafe*/(observer));
+ }
+
+ [SecurityCritical]
+ public override object InitializeLifetimeService()
+ {
+ return lease;
+ }
+
+ sealed class RemotableSubscription : MarshalByRefObject, IDisposable, ISponsor
+ {
+ private IDisposable underlyingSubscription;
+
+ public RemotableSubscription(IDisposable underlyingSubscription)
+ {
+ this.underlyingSubscription = underlyingSubscription;
+ }
+
+ public void Dispose()
+ {
+ //
+ // Avoiding double-dispose and dropping the reference upon disposal.
+ //
+ using (Interlocked.Exchange(ref underlyingSubscription, Disposable.Empty))
+ {
+ Unregister();
+ }
+ }
+
+ [SecuritySafeCritical] // See remarks at the top of the file.
+ private void Unregister()
+ {
+ var lease = (ILease)RemotingServices.GetLifetimeService(this);
+ if (lease != null)
+ lease.Unregister(this);
+ }
+
+ [SecurityCritical]
+ public override object InitializeLifetimeService()
+ {
+ var lease = (ILease)base.InitializeLifetimeService();
+ lease.Register(this);
+ return lease;
+ }
+
+ [SecurityCritical]
+ TimeSpan ISponsor.Renewal(ILease lease)
+ {
+ return lease.InitialLeaseTime;
+ }
+ }
+ }
+
+ #endregion
+ }
+}
+#endif \ No newline at end of file