1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
6 namespace System.ServiceModel.Channels
9 using System.ServiceModel;
10 using System.ServiceModel.Description;
12 using System.Collections.Generic;
13 using System.Runtime.Serialization;
14 using System.ServiceModel.Diagnostics;
15 using System.ServiceModel.Dispatcher;
16 using System.Globalization;
18 public abstract class MessageFault
20 static MessageFault defaultMessageFault;
22 public static MessageFault CreateFault(FaultCode code, string reason)
24 return CreateFault(code, new FaultReason(reason));
27 public static MessageFault CreateFault(FaultCode code, FaultReason reason)
29 return CreateFault(code, reason, null, null, "", "");
32 public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail)
34 return CreateFault(code, reason, detail, DataContractSerializerDefaults.CreateSerializer(
35 (detail == null ? typeof(object) : detail.GetType()), int.MaxValue/*maxItems*/), "", "");
38 public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer)
40 return CreateFault(code, reason, detail, serializer, "", "");
43 public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor)
45 if (serializer == null)
46 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("serializer"));
47 return CreateFault(code, reason, detail, serializer, actor, actor);
50 public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor, string node)
53 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("code"));
55 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("reason"));
57 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("actor"));
59 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("node"));
60 return new XmlObjectSerializerFault(code, reason, detail, serializer, actor, node);
63 public static MessageFault CreateFault(Message message, int maxBufferSize)
66 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("message"));
67 XmlDictionaryReader reader = message.GetReaderAtBodyContents();
72 #pragma warning suppress 56506 // Microsoft, Message.Version can never be null
73 EnvelopeVersion envelopeVersion = message.Version.Envelope;
75 if (envelopeVersion == EnvelopeVersion.Soap12)
77 fault = ReceivedFault.CreateFault12(reader, maxBufferSize);
79 else if (envelopeVersion == EnvelopeVersion.Soap11)
81 fault = ReceivedFault.CreateFault11(reader, maxBufferSize);
83 else if (envelopeVersion == EnvelopeVersion.None)
85 fault = ReceivedFault.CreateFaultNone(reader, maxBufferSize);
89 throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.EnvelopeVersionUnknown, envelopeVersion.ToString())), message);
91 message.ReadFromBodyContentsToEnd(reader);
94 catch (InvalidOperationException e)
96 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
97 SR.GetString(SR.SFxErrorDeserializingFault), e));
99 catch (FormatException e)
101 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
102 SR.GetString(SR.SFxErrorDeserializingFault), e));
104 catch (XmlException e)
106 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
107 SR.GetString(SR.SFxErrorDeserializingFault), e));
112 internal static MessageFault Default
116 if (defaultMessageFault == null)
118 defaultMessageFault = MessageFault.CreateFault(new FaultCode("Default"), new FaultReason("", CultureInfo.CurrentCulture));
120 return defaultMessageFault;
124 public virtual string Actor
132 public abstract FaultCode Code { get; }
134 public bool IsMustUnderstandFault
138 FaultCode code = this.Code;
139 if (String.Compare(code.Name, MessageStrings.MustUnderstandFault, StringComparison.Ordinal) != 0)
144 if ((String.Compare(code.Namespace, EnvelopeVersion.Soap11.Namespace, StringComparison.Ordinal) != 0) &&
145 (String.Compare(code.Namespace, EnvelopeVersion.Soap12.Namespace, StringComparison.Ordinal) != 0))
154 public virtual string Node
162 public abstract bool HasDetail { get; }
164 public abstract FaultReason Reason { get; }
166 public T GetDetail<T>()
168 return GetDetail<T>(DataContractSerializerDefaults.CreateSerializer(typeof(T), int.MaxValue/*maxItems*/));
171 public T GetDetail<T>(XmlObjectSerializer serializer)
173 if (serializer == null)
174 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("serializer"));
175 XmlDictionaryReader reader = GetReaderAtDetailContents();
176 T value = (T)serializer.ReadObject(reader);
179 reader.MoveToContent();
180 if (reader.NodeType != XmlNodeType.EndElement && !reader.EOF)
181 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.ExtraContentIsPresentInFaultDetail)));
186 public XmlDictionaryReader GetReaderAtDetailContents()
189 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FaultDoesNotHaveAnyDetail)));
190 return OnGetReaderAtDetailContents();
193 protected virtual void OnWriteDetail(XmlDictionaryWriter writer, EnvelopeVersion version)
195 OnWriteStartDetail(writer, version);
196 OnWriteDetailContents(writer);
197 writer.WriteEndElement();
200 protected virtual void OnWriteStartDetail(XmlDictionaryWriter writer, EnvelopeVersion version)
202 if (version == EnvelopeVersion.Soap12)
203 writer.WriteStartElement(XD.Message12Dictionary.FaultDetail, XD.Message12Dictionary.Namespace);
204 else if (version == EnvelopeVersion.Soap11)
205 writer.WriteStartElement(XD.Message11Dictionary.FaultDetail, XD.Message11Dictionary.FaultNamespace);
207 writer.WriteStartElement(XD.Message12Dictionary.FaultDetail, XD.MessageDictionary.Namespace);
210 protected abstract void OnWriteDetailContents(XmlDictionaryWriter writer);
212 protected virtual XmlDictionaryReader OnGetReaderAtDetailContents()
214 XmlBuffer detailBuffer = new XmlBuffer(int.MaxValue);
215 XmlDictionaryWriter writer = detailBuffer.OpenSection(XmlDictionaryReaderQuotas.Max);
216 OnWriteDetail(writer, EnvelopeVersion.Soap12); // Wrap in soap 1.2 by default
217 detailBuffer.CloseSection();
218 detailBuffer.Close();
219 XmlDictionaryReader reader = detailBuffer.GetReader(0);
220 reader.Read(); // Skip the detail element
224 public static bool WasHeaderNotUnderstood(MessageHeaders headers, string name, string ns)
228 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("headers");
231 for (int i = 0; i < headers.Count; i++)
233 MessageHeaderInfo headerInfo = headers[i];
234 if ((String.Compare(headerInfo.Name, Message12Strings.NotUnderstood, StringComparison.Ordinal) == 0) &&
235 (String.Compare(headerInfo.Namespace, Message12Strings.Namespace, StringComparison.Ordinal) == 0))
237 using (XmlDictionaryReader reader = headers.GetReaderAtHeader(i))
239 reader.MoveToAttribute(Message12Strings.QName, Message12Strings.Namespace);
242 string actualNamespace;
243 reader.ReadContentAsQualifiedName(out actualName, out actualNamespace);
245 if ((actualName != null) &&
246 (actualNamespace != null) &&
247 (String.Compare(name, actualName, StringComparison.Ordinal) == 0) &&
248 (String.Compare(ns, actualNamespace, StringComparison.Ordinal) == 0))
259 public void WriteTo(XmlWriter writer, EnvelopeVersion version)
261 WriteTo(XmlDictionaryWriter.CreateDictionaryWriter(writer), version);
264 public void WriteTo(XmlDictionaryWriter writer, EnvelopeVersion version)
268 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
273 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version");
276 if (version == EnvelopeVersion.Soap12)
280 else if (version == EnvelopeVersion.Soap11)
284 else if (version == EnvelopeVersion.None)
290 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.EnvelopeVersionUnknown, version.ToString())));
294 void WriteToNone(XmlDictionaryWriter writer)
296 WriteTo12Driver(writer, EnvelopeVersion.None);
299 void WriteTo12Driver(XmlDictionaryWriter writer, EnvelopeVersion version)
301 writer.WriteStartElement(XD.MessageDictionary.Fault, version.DictionaryNamespace);
302 writer.WriteStartElement(XD.Message12Dictionary.FaultCode, version.DictionaryNamespace);
303 WriteFaultCode12Driver(writer, Code, version);
304 writer.WriteEndElement();
305 writer.WriteStartElement(XD.Message12Dictionary.FaultReason, version.DictionaryNamespace);
306 FaultReason reason = Reason;
307 for (int i = 0; i < reason.Translations.Count; i++)
309 FaultReasonText text = reason.Translations[i];
310 writer.WriteStartElement(XD.Message12Dictionary.FaultText, version.DictionaryNamespace);
311 writer.WriteAttributeString("xml", "lang", XmlUtil.XmlNs, text.XmlLang);
312 writer.WriteString(text.Text);
313 writer.WriteEndElement();
315 writer.WriteEndElement();
317 writer.WriteElementString(XD.Message12Dictionary.FaultNode, version.DictionaryNamespace, Node);
318 if (Actor.Length > 0)
319 writer.WriteElementString(XD.Message12Dictionary.FaultRole, version.DictionaryNamespace, Actor);
322 OnWriteDetail(writer, version);
324 writer.WriteEndElement();
327 void WriteFaultCode12Driver(XmlDictionaryWriter writer, FaultCode faultCode, EnvelopeVersion version)
329 writer.WriteStartElement(XD.Message12Dictionary.FaultValue, version.DictionaryNamespace);
331 if (faultCode.IsSenderFault)
332 name = version.SenderFaultName;
333 else if (faultCode.IsReceiverFault)
334 name = version.ReceiverFaultName;
336 name = faultCode.Name;
338 if (faultCode.IsPredefinedFault)
339 ns = version.Namespace;
341 ns = faultCode.Namespace;
342 string prefix = writer.LookupPrefix(ns);
344 writer.WriteAttributeString("xmlns", "a", XmlUtil.XmlNsNs, ns);
345 writer.WriteQualifiedName(name, ns);
346 writer.WriteEndElement();
348 if (faultCode.SubCode != null)
350 writer.WriteStartElement(XD.Message12Dictionary.FaultSubcode, version.DictionaryNamespace);
351 WriteFaultCode12Driver(writer, faultCode.SubCode, version);
352 writer.WriteEndElement();
356 void WriteTo12(XmlDictionaryWriter writer)
358 WriteTo12Driver(writer, EnvelopeVersion.Soap12);
361 void WriteTo11(XmlDictionaryWriter writer)
363 writer.WriteStartElement(XD.MessageDictionary.Fault, XD.Message11Dictionary.Namespace);
364 writer.WriteStartElement(XD.Message11Dictionary.FaultCode, XD.Message11Dictionary.FaultNamespace);
366 FaultCode faultCode = Code;
367 if (faultCode.SubCode != null)
368 faultCode = faultCode.SubCode;
371 if (faultCode.IsSenderFault)
373 else if (faultCode.IsReceiverFault)
376 name = faultCode.Name;
378 if (faultCode.IsPredefinedFault)
379 ns = Message11Strings.Namespace;
381 ns = faultCode.Namespace;
382 string prefix = writer.LookupPrefix(ns);
384 writer.WriteAttributeString("xmlns", "a", XmlUtil.XmlNsNs, ns);
385 writer.WriteQualifiedName(name, ns);
386 writer.WriteEndElement();
387 FaultReasonText translation = Reason.Translations[0];
388 writer.WriteStartElement(XD.Message11Dictionary.FaultString, XD.Message11Dictionary.FaultNamespace);
389 if (translation.XmlLang.Length > 0)
390 writer.WriteAttributeString("xml", "lang", XmlUtil.XmlNs, translation.XmlLang);
391 writer.WriteString(translation.Text);
392 writer.WriteEndElement();
393 if (Actor.Length > 0)
394 writer.WriteElementString(XD.Message11Dictionary.FaultActor, XD.Message11Dictionary.FaultNamespace, Actor);
397 OnWriteDetail(writer, EnvelopeVersion.Soap11);
399 writer.WriteEndElement();
403 class XmlObjectSerializerFault : MessageFault
410 XmlObjectSerializer serializer;
412 public XmlObjectSerializerFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor, string node)
415 this.reason = reason;
416 this.detail = detail;
417 this.serializer = serializer;
422 public override string Actor
430 public override FaultCode Code
438 public override bool HasDetail
442 return serializer != null;
446 public override string Node
454 public override FaultReason Reason
470 protected override void OnWriteDetailContents(XmlDictionaryWriter writer)
472 if (serializer != null)
476 serializer.WriteObject(writer, detail);
482 class ReceivedFault : MessageFault
490 EnvelopeVersion receivedVersion;
492 ReceivedFault(FaultCode code, FaultReason reason, string actor, string node, XmlBuffer detail, EnvelopeVersion version)
495 this.reason = reason;
498 this.receivedVersion = version;
499 this.hasDetail = InferHasDetail(detail);
500 this.detail = this.hasDetail ? detail : null;
503 public override string Actor
511 public override FaultCode Code
519 public override bool HasDetail
527 public override string Node
535 public override FaultReason Reason
543 bool InferHasDetail(XmlBuffer detail)
545 bool hasDetail = false;
548 XmlDictionaryReader reader = detail.GetReader(0);
549 if (!reader.IsEmptyElement && reader.Read()) // check if the detail element contains data
550 hasDetail = (reader.MoveToContent() != XmlNodeType.EndElement);
556 protected override void OnWriteDetail(XmlDictionaryWriter writer, EnvelopeVersion version)
558 using (XmlReader r = detail.GetReader(0))
561 base.OnWriteStartDetail(writer, version);
563 // Copy the attributes
564 while (r.MoveToNextAttribute())
566 if (ShouldWriteDetailAttribute(version, r.Prefix, r.LocalName, r.Value))
568 writer.WriteAttributeString(r.Prefix, r.LocalName, r.NamespaceURI, r.Value);
576 while (r.NodeType != XmlNodeType.EndElement)
577 writer.WriteNode(r, false);
580 writer.WriteEndElement();
584 protected override void OnWriteStartDetail(XmlDictionaryWriter writer, EnvelopeVersion version)
586 using (XmlReader r = detail.GetReader(0))
589 base.OnWriteStartDetail(writer, version);
591 // Copy the attributes
592 while (r.MoveToNextAttribute())
594 if (ShouldWriteDetailAttribute(version, r.Prefix, r.LocalName, r.Value))
596 writer.WriteAttributeString(r.Prefix, r.LocalName, r.NamespaceURI, r.Value);
602 protected override void OnWriteDetailContents(XmlDictionaryWriter writer)
604 using (XmlReader r = detail.GetReader(0))
607 while (r.NodeType != XmlNodeType.EndElement)
608 writer.WriteNode(r, false);
612 protected override XmlDictionaryReader OnGetReaderAtDetailContents()
614 XmlDictionaryReader reader = detail.GetReader(0);
615 reader.Read(); // Skip the detail element
619 bool ShouldWriteDetailAttribute(EnvelopeVersion targetVersion, string prefix, string localName, string attributeValue)
621 // Handle fault detail version conversion from Soap12 to Soap11 -- scope tightly to only conversion from Soap12 -> Soap11
622 // SOAP 1.1 specifications allow an arbitrary element within <fault>, hence:
623 // transform this IFF the SOAP namespace specified will affect the namespace of the <detail> element,
624 // AND the namespace specified is exactly the Soap12 Namespace.
625 bool shouldSkip = this.receivedVersion == EnvelopeVersion.Soap12 // original incoming version
626 && targetVersion == EnvelopeVersion.Soap11 // version to serialize to
627 && string.IsNullOrEmpty(prefix) // attribute prefix
628 && localName == "xmlns" // only transform namespace attributes, don't care about others
629 && attributeValue == XD.Message12Dictionary.Namespace.Value;
634 public static ReceivedFault CreateFaultNone(XmlDictionaryReader reader, int maxBufferSize)
636 return CreateFault12Driver(reader, maxBufferSize, EnvelopeVersion.None);
639 static ReceivedFault CreateFault12Driver(XmlDictionaryReader reader, int maxBufferSize, EnvelopeVersion version)
641 reader.ReadStartElement(XD.MessageDictionary.Fault, version.DictionaryNamespace);
642 reader.ReadStartElement(XD.Message12Dictionary.FaultCode, version.DictionaryNamespace);
643 FaultCode code = ReadFaultCode12Driver(reader, version);
644 reader.ReadEndElement();
645 List<FaultReasonText> translations = new List<FaultReasonText>();
646 if (reader.IsEmptyElement)
648 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.AtLeastOneFaultReasonMustBeSpecified)));
652 reader.ReadStartElement(XD.Message12Dictionary.FaultReason, version.DictionaryNamespace);
653 while (reader.IsStartElement(XD.Message12Dictionary.FaultText, version.DictionaryNamespace))
654 translations.Add(ReadTranslation12(reader));
655 reader.ReadEndElement();
660 if (reader.IsStartElement(XD.Message12Dictionary.FaultNode, version.DictionaryNamespace))
661 node = reader.ReadElementContentAsString();
662 if (reader.IsStartElement(XD.Message12Dictionary.FaultRole, version.DictionaryNamespace))
663 actor = reader.ReadElementContentAsString();
664 XmlBuffer detail = null;
665 if (reader.IsStartElement(XD.Message12Dictionary.FaultDetail, version.DictionaryNamespace))
667 detail = new XmlBuffer(maxBufferSize);
668 XmlDictionaryWriter writer = detail.OpenSection(reader.Quotas);
669 writer.WriteNode(reader, false);
670 detail.CloseSection();
673 reader.ReadEndElement();
674 FaultReason reason = new FaultReason(translations);
675 return new ReceivedFault(code, reason, actor, node, detail, version);
678 static FaultCode ReadFaultCode12Driver(XmlDictionaryReader reader, EnvelopeVersion version)
682 FaultCode subCode = null;
683 reader.ReadStartElement(XD.Message12Dictionary.FaultValue, version.DictionaryNamespace);
684 XmlUtil.ReadContentAsQName(reader, out localName, out ns);
685 reader.ReadEndElement();
686 if (reader.IsStartElement(XD.Message12Dictionary.FaultSubcode, version.DictionaryNamespace))
688 reader.ReadStartElement();
689 subCode = ReadFaultCode12Driver(reader, version);
690 reader.ReadEndElement();
691 return new FaultCode(localName, ns, subCode);
693 return new FaultCode(localName, ns);
696 public static ReceivedFault CreateFault12(XmlDictionaryReader reader, int maxBufferSize)
698 return CreateFault12Driver(reader, maxBufferSize, EnvelopeVersion.Soap12);
701 static FaultReasonText ReadTranslation12(XmlDictionaryReader reader)
703 string xmlLang = XmlUtil.GetXmlLangAttribute(reader);
704 string text = reader.ReadElementContentAsString();
705 return new FaultReasonText(text, xmlLang);
708 public static ReceivedFault CreateFault11(XmlDictionaryReader reader, int maxBufferSize)
710 reader.ReadStartElement(XD.MessageDictionary.Fault, XD.Message11Dictionary.Namespace);
713 reader.ReadStartElement(XD.Message11Dictionary.FaultCode, XD.Message11Dictionary.FaultNamespace);
714 XmlUtil.ReadContentAsQName(reader, out name, out ns);
715 FaultCode code = new FaultCode(name, ns);
716 reader.ReadEndElement();
718 string xmlLang = reader.XmlLang;
719 reader.MoveToContent(); // Don't do IsStartElement. FaultString is required, so let the reader throw.
720 string text = reader.ReadElementContentAsString(XD.Message11Dictionary.FaultString.Value, XD.Message11Dictionary.FaultNamespace.Value);
721 FaultReasonText translation = new FaultReasonText(text, xmlLang);
724 if (reader.IsStartElement(XD.Message11Dictionary.FaultActor, XD.Message11Dictionary.FaultNamespace))
725 actor = reader.ReadElementContentAsString();
726 XmlBuffer detail = null;
727 if (reader.IsStartElement(XD.Message11Dictionary.FaultDetail, XD.Message11Dictionary.FaultNamespace))
729 detail = new XmlBuffer(maxBufferSize);
730 XmlDictionaryWriter writer = detail.OpenSection(reader.Quotas);
731 writer.WriteNode(reader, false);
732 detail.CloseSection();
735 reader.ReadEndElement();
736 FaultReason reason = new FaultReason(translation);
737 return new ReceivedFault(code, reason, actor, actor, detail, EnvelopeVersion.Soap11);