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

QueryLanguage.Remoting.cs « Linq « Reactive « System.Reactive.Runtime.Remoting « Source « NET « Rx - github.com/mono/rx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 9d85a41c4f5f8cfdd5d980845138c3390cb68ed6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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