| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Collections; |
| | | 6 | | using System.Xml; |
| | | 7 | | using CoreWCF.Diagnostics; |
| | | 8 | | using CoreWCF.Runtime; |
| | | 9 | | |
| | | 10 | | namespace CoreWCF.Channels |
| | | 11 | | { |
| | | 12 | | internal class RequestReplyCorrelator : IRequestReplyCorrelator |
| | | 13 | | { |
| | | 14 | | private readonly Hashtable _states; |
| | | 15 | | |
| | 660 | 16 | | internal RequestReplyCorrelator() |
| | | 17 | | { |
| | 660 | 18 | | _states = new Hashtable(); |
| | 660 | 19 | | } |
| | | 20 | | |
| | | 21 | | void IRequestReplyCorrelator.Add<T>(Message request, T state) |
| | | 22 | | { |
| | 1 | 23 | | UniqueId messageId = request.Headers.MessageId; |
| | 1 | 24 | | Type stateType = typeof(T); |
| | 1 | 25 | | Key key = new Key(messageId, stateType); |
| | | 26 | | |
| | | 27 | | // add the correlator key to the request, this will be needed for cleaning up the correlator table in case o |
| | | 28 | | // channel aborting or faulting while there are pending requests |
| | 1 | 29 | | if (state is ICorrelatorKey value) |
| | | 30 | | { |
| | 1 | 31 | | value.RequestCorrelatorKey = key; |
| | | 32 | | } |
| | | 33 | | |
| | 1 | 34 | | lock (_states) |
| | | 35 | | { |
| | 1 | 36 | | _states.Add(key, state); |
| | 1 | 37 | | } |
| | 1 | 38 | | } |
| | | 39 | | |
| | | 40 | | T IRequestReplyCorrelator.Find<T>(Message reply, bool remove) |
| | | 41 | | { |
| | 1 | 42 | | UniqueId relatesTo = GetRelatesTo(reply); |
| | 1 | 43 | | Type stateType = typeof(T); |
| | 1 | 44 | | Key key = new Key(relatesTo, stateType); |
| | 1 | 45 | | T value = (T)_states[key]; |
| | | 46 | | |
| | 1 | 47 | | if (remove) |
| | | 48 | | { |
| | | 49 | | // With HashTable, only need to lock when modifying |
| | 1 | 50 | | lock (_states) |
| | | 51 | | { |
| | 1 | 52 | | _states.Remove(key); |
| | 1 | 53 | | } |
| | | 54 | | } |
| | | 55 | | |
| | 1 | 56 | | return value; |
| | | 57 | | } |
| | | 58 | | |
| | | 59 | | // This method is used to remove the request from the correlator table when the |
| | | 60 | | // reply is lost. This will avoid leaking the correlator table in cases where the |
| | | 61 | | // channel faults or aborts while there are pending requests. |
| | | 62 | | internal void RemoveRequest(ICorrelatorKey request) |
| | | 63 | | { |
| | | 64 | | Fx.Assert(request != null, "request cannot be null"); |
| | 0 | 65 | | if (request.RequestCorrelatorKey != null) |
| | | 66 | | { |
| | 0 | 67 | | lock (_states) |
| | | 68 | | { |
| | 0 | 69 | | _states.Remove(request.RequestCorrelatorKey); |
| | 0 | 70 | | } |
| | | 71 | | } |
| | 0 | 72 | | } |
| | | 73 | | |
| | | 74 | | private UniqueId GetRelatesTo(Message reply) |
| | | 75 | | { |
| | 1 | 76 | | UniqueId relatesTo = reply.Headers.RelatesTo; |
| | 1 | 77 | | if (relatesTo == null) |
| | | 78 | | { |
| | 0 | 79 | | throw TraceUtility.ThrowHelperError(new ArgumentException(SR.SuppliedMessageIsNotAReplyItHasNoRelatesTo0 |
| | | 80 | | } |
| | | 81 | | |
| | 1 | 82 | | return relatesTo; |
| | | 83 | | } |
| | | 84 | | |
| | | 85 | | internal static bool AddressReply(Message reply, Message request) |
| | | 86 | | { |
| | 1 | 87 | | ReplyToInfo info = ExtractReplyToInfo(request); |
| | 1 | 88 | | return AddressReply(reply, info); |
| | | 89 | | } |
| | | 90 | | |
| | | 91 | | internal static bool AddressReply(Message reply, ReplyToInfo info) |
| | | 92 | | { |
| | 581 | 93 | | EndpointAddress destination = null; |
| | | 94 | | |
| | 581 | 95 | | if (info.HasFaultTo && (reply.IsFault)) |
| | | 96 | | { |
| | 0 | 97 | | destination = info.FaultTo; |
| | | 98 | | } |
| | 581 | 99 | | else if (info.HasReplyTo) |
| | | 100 | | { |
| | 0 | 101 | | destination = info.ReplyTo; |
| | | 102 | | } |
| | | 103 | | |
| | 581 | 104 | | if (destination != null) |
| | | 105 | | { |
| | 0 | 106 | | destination.ApplyTo(reply); |
| | 0 | 107 | | return !destination.IsNone; |
| | | 108 | | } |
| | | 109 | | else |
| | | 110 | | { |
| | 581 | 111 | | return true; |
| | | 112 | | } |
| | | 113 | | } |
| | | 114 | | |
| | | 115 | | internal static ReplyToInfo ExtractReplyToInfo(Message message) |
| | | 116 | | { |
| | 1 | 117 | | return new ReplyToInfo(message); |
| | | 118 | | } |
| | | 119 | | |
| | | 120 | | internal static void PrepareRequest(Message request) |
| | | 121 | | { |
| | 2 | 122 | | MessageHeaders requestHeaders = request.Headers; |
| | | 123 | | |
| | 2 | 124 | | if (requestHeaders.MessageId == null) |
| | | 125 | | { |
| | 1 | 126 | | requestHeaders.MessageId = new UniqueId(); |
| | | 127 | | } |
| | | 128 | | |
| | 2 | 129 | | request.Properties.AllowOutputBatching = false; |
| | | 130 | | //if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity) |
| | | 131 | | //{ |
| | | 132 | | // TraceUtility.AddAmbientActivityToMessage(request); |
| | | 133 | | //} |
| | 2 | 134 | | } |
| | | 135 | | |
| | | 136 | | internal static void PrepareReply(Message reply, UniqueId messageId) |
| | | 137 | | { |
| | 296 | 138 | | if (ReferenceEquals(messageId, null)) |
| | | 139 | | { |
| | 0 | 140 | | throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.MissingMessageID), reply); |
| | | 141 | | } |
| | | 142 | | |
| | 296 | 143 | | MessageHeaders replyHeaders = reply.Headers; |
| | | 144 | | |
| | 296 | 145 | | if (ReferenceEquals(replyHeaders.RelatesTo, null)) |
| | | 146 | | { |
| | 296 | 147 | | replyHeaders.RelatesTo = messageId; |
| | | 148 | | } |
| | | 149 | | |
| | | 150 | | //if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity) |
| | | 151 | | //{ |
| | | 152 | | // TraceUtility.AddAmbientActivityToMessage(reply); |
| | | 153 | | //} |
| | 296 | 154 | | } |
| | | 155 | | |
| | | 156 | | internal static void PrepareReply(Message reply, Message request) |
| | | 157 | | { |
| | 0 | 158 | | UniqueId messageId = request.Headers.MessageId; |
| | | 159 | | |
| | 0 | 160 | | if (messageId != null) |
| | | 161 | | { |
| | 0 | 162 | | MessageHeaders replyHeaders = reply.Headers; |
| | | 163 | | |
| | 0 | 164 | | if (ReferenceEquals(replyHeaders.RelatesTo, null)) |
| | | 165 | | { |
| | 0 | 166 | | replyHeaders.RelatesTo = messageId; |
| | | 167 | | } |
| | | 168 | | } |
| | | 169 | | |
| | | 170 | | //if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity) |
| | | 171 | | //{ |
| | | 172 | | // TraceUtility.AddAmbientActivityToMessage(reply); |
| | | 173 | | //} |
| | 0 | 174 | | } |
| | | 175 | | |
| | | 176 | | internal struct ReplyToInfo |
| | | 177 | | { |
| | | 178 | | internal ReplyToInfo(Message message) |
| | | 179 | | { |
| | 684 | 180 | | FaultTo = message.Headers.FaultTo; |
| | 684 | 181 | | ReplyTo = message.Headers.ReplyTo; |
| | 684 | 182 | | if (message.Version.Addressing == AddressingVersion.WSAddressingAugust2004) |
| | | 183 | | { |
| | 15 | 184 | | From = message.Headers.From; |
| | | 185 | | } |
| | | 186 | | else |
| | | 187 | | { |
| | 669 | 188 | | From = null; |
| | | 189 | | } |
| | 669 | 190 | | } |
| | | 191 | | |
| | 581 | 192 | | internal EndpointAddress FaultTo { get; } |
| | | 193 | | |
| | 0 | 194 | | internal EndpointAddress From { get; } |
| | | 195 | | |
| | | 196 | | internal bool HasFaultTo |
| | | 197 | | { |
| | 581 | 198 | | get { return !IsTrivial(FaultTo); } |
| | | 199 | | } |
| | | 200 | | |
| | | 201 | | internal bool HasFrom |
| | | 202 | | { |
| | 0 | 203 | | get { return !IsTrivial(From); } |
| | | 204 | | } |
| | | 205 | | |
| | | 206 | | internal bool HasReplyTo |
| | | 207 | | { |
| | 581 | 208 | | get { return !IsTrivial(ReplyTo); } |
| | | 209 | | } |
| | | 210 | | |
| | 1264 | 211 | | internal EndpointAddress ReplyTo { get; } |
| | | 212 | | |
| | | 213 | | private bool IsTrivial(EndpointAddress address) |
| | | 214 | | { |
| | | 215 | | // Note: even if address.IsAnonymous, it may have identity, reference parameters, etc. |
| | 1162 | 216 | | return (address == null) || (address == EndpointAddress.AnonymousAddress); |
| | | 217 | | } |
| | | 218 | | } |
| | | 219 | | |
| | | 220 | | internal class Key |
| | | 221 | | { |
| | | 222 | | internal UniqueId MessageId; |
| | | 223 | | internal Type StateType; |
| | | 224 | | |
| | 2 | 225 | | internal Key(UniqueId messageId, Type stateType) |
| | | 226 | | { |
| | 2 | 227 | | MessageId = messageId; |
| | 2 | 228 | | StateType = stateType; |
| | 2 | 229 | | } |
| | | 230 | | |
| | | 231 | | public override bool Equals(object obj) |
| | | 232 | | { |
| | 2 | 233 | | if (!(obj is Key other)) |
| | | 234 | | { |
| | 0 | 235 | | return false; |
| | | 236 | | } |
| | | 237 | | |
| | 2 | 238 | | return other.MessageId == MessageId && other.StateType == StateType; |
| | | 239 | | } |
| | | 240 | | |
| | | 241 | | public override int GetHashCode() |
| | | 242 | | { |
| | 3 | 243 | | return MessageId.GetHashCode() ^ StateType.GetHashCode(); |
| | | 244 | | } |
| | | 245 | | |
| | | 246 | | public override string ToString() |
| | | 247 | | { |
| | 0 | 248 | | return typeof(Key).ToString() + ": {" + MessageId + ", " + StateType.ToString() + "}"; |
| | | 249 | | } |
| | | 250 | | } |
| | | 251 | | } |
| | | 252 | | } |