Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Web.Services / System / Web / Services / Discovery / DiscoveryDocumentReference.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DiscoveryDocumentReference.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 namespace System.Web.Services.Discovery {
8
9     using System;
10     using System.Net;
11     using System.Xml;
12     using System.Diagnostics;
13     using System.IO;
14     using System.Xml.Serialization;
15     using System.Web.Services.Protocols;
16     using System.Web.Services.Configuration;
17     using System.Text;
18     using System.Globalization;
19     using System.Threading;
20     using System.Collections;
21     using System.Web.Services.Diagnostics;
22
23     /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference"]/*' />
24     /// <devdoc>
25     ///    <para>[To be supplied.]</para>
26     /// </devdoc>
27     [XmlRoot("discoveryRef", Namespace = DiscoveryDocument.Namespace)]
28     public sealed class DiscoveryDocumentReference : DiscoveryReference {
29
30         private string reference;
31
32         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.DiscoveryDocumentReference"]/*' />
33         /// <devdoc>
34         ///    <para>[To be supplied.]</para>
35         /// </devdoc>
36         public DiscoveryDocumentReference() {
37         }
38
39         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.DiscoveryDocumentReference1"]/*' />
40         /// <devdoc>
41         ///    <para>[To be supplied.]</para>
42         /// </devdoc>
43         public DiscoveryDocumentReference(string href) {
44             Ref = href;
45         }
46
47         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.Ref"]/*' />
48         /// <devdoc>
49         ///    <para>[To be supplied.]</para>
50         /// </devdoc>
51         [XmlAttribute("ref")]
52         public string Ref {
53             get {
54                 return reference == null ? "" : reference;
55             }
56             set {
57                 reference = value;
58             }
59         }
60
61         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.DefaultFilename"]/*' />
62         /// <devdoc>
63         ///    <para>[To be supplied.]</para>
64         /// </devdoc>
65         [XmlIgnore]
66         public override string DefaultFilename {
67             get {
68                 string filename = FilenameFromUrl(Url);
69                 return Path.ChangeExtension(filename, ".disco");        // [[....]] change default extension
70             }
71         }
72
73         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.Document"]/*' />
74         /// <devdoc>
75         ///    <para>[To be supplied.]</para>
76         /// </devdoc>
77         [XmlIgnore]
78         public DiscoveryDocument Document {
79             get {
80                 if (ClientProtocol == null)
81                     throw new InvalidOperationException(Res.GetString(Res.WebMissingClientProtocol));
82                 object document = ClientProtocol.Documents[Url];
83                 if (document == null) {
84                     Resolve();
85                     document = ClientProtocol.Documents[Url];
86                 }
87                 DiscoveryDocument discoDocument = document as DiscoveryDocument;
88                 if (discoDocument == null) {
89                     throw new InvalidOperationException(Res.GetString(Res.WebInvalidDocType,
90                                                       typeof(DiscoveryDocument).FullName,
91                                                       document == null ? string.Empty : document.GetType().FullName,
92                                                       Url));
93                 }
94                 return discoDocument;
95             }
96         }
97
98         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.WriteDocument"]/*' />
99         /// <devdoc>
100         ///    <para>[To be supplied.]</para>
101         /// </devdoc>
102         public override void WriteDocument(object document, Stream stream) {
103             WebServicesSection.Current.DiscoveryDocumentSerializer.Serialize(new StreamWriter(stream, new UTF8Encoding(false)), document);
104         }
105
106         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.ReadDocument"]/*' />
107         /// <devdoc>
108         ///    <para>[To be supplied.]</para>
109         /// </devdoc>
110         public override object ReadDocument(Stream stream) {
111             return WebServicesSection.Current.DiscoveryDocumentSerializer.Deserialize(stream);
112         }
113
114         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.Url"]/*' />
115         /// <devdoc>
116         ///    <para>[To be supplied.]</para>
117         /// </devdoc>
118         [XmlIgnore]
119         public override string Url {
120             get { return Ref; }
121             set { Ref = value; }
122         }
123
124         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.GetDocumentNoParse"]/*' />
125         /// <devdoc>
126         /// Retrieves a discovery document from Url, either out of the ClientProtocol
127         /// or from a stream. Does not
128         /// </devdoc>
129         private static DiscoveryDocument GetDocumentNoParse(ref string url, DiscoveryClientProtocol client) {
130             DiscoveryDocument d = (DiscoveryDocument) client.Documents[url];
131             if (d != null) {
132                 return d;
133             }
134
135             string contentType = null;
136
137             Stream stream = client.Download(ref url, ref contentType);
138             try {
139                 XmlTextReader reader = new XmlTextReader(new StreamReader(stream, RequestResponseUtils.GetEncoding(contentType)));
140                 reader.WhitespaceHandling = WhitespaceHandling.Significant;
141                 reader.XmlResolver = null;
142                 reader.DtdProcessing = DtdProcessing.Prohibit;
143                 if (!DiscoveryDocument.CanRead(reader)) {
144                     // there is no discovery document at this location
145                     ArgumentException exception = new ArgumentException(Res.GetString(Res.WebInvalidFormat));
146                     throw new InvalidOperationException(Res.GetString(Res.WebMissingDocument, url), exception);
147                 }
148                 return DiscoveryDocument.Read(reader);
149             }
150             finally {
151                 stream.Close();
152             }
153         }
154
155         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.Resolve"]/*' />
156         /// <devdoc>
157         ///    <para>[To be supplied.]</para>
158         /// </devdoc>
159         protected internal override void Resolve(string contentType, Stream stream) {
160             DiscoveryDocument document = null;
161
162             if (ContentType.IsHtml(contentType)) {
163                 string newRef = LinkGrep.SearchForLink(stream);
164                 if (newRef != null) {
165                     string newUrl = UriToString(Url, newRef);
166                     document = GetDocumentNoParse(ref newUrl, ClientProtocol);
167                     Url = newUrl;
168                 }
169                 else
170                     throw new InvalidContentTypeException(Res.GetString(Res.WebInvalidContentType, contentType), contentType);
171             }
172
173             if (document == null) { // probably xml...
174                 XmlTextReader reader = new XmlTextReader(new StreamReader(stream, RequestResponseUtils.GetEncoding(contentType)));
175                 reader.XmlResolver = null;
176                 reader.WhitespaceHandling = WhitespaceHandling.Significant;
177                 reader.DtdProcessing = DtdProcessing.Prohibit;
178                 if (DiscoveryDocument.CanRead(reader)) {
179                     // it's a discovery document, so just read it.
180                     document = DiscoveryDocument.Read(reader);
181                 }
182                 else {
183                     // check out the processing instructions before the first tag.  if any of them
184                     // match the form specified in the DISCO spec, save the href.
185                     stream.Position = 0;
186                     XmlTextReader newReader = new XmlTextReader(new StreamReader(stream, RequestResponseUtils.GetEncoding(contentType)));
187                     newReader.XmlResolver = null;
188                     newReader.DtdProcessing = DtdProcessing.Prohibit;
189                     while (newReader.NodeType != XmlNodeType.Element) {
190                         if (newReader.NodeType == XmlNodeType.ProcessingInstruction) {
191                             // manually parse the PI contents since XmlTextReader won't automatically do it
192                             StringBuilder sb = new StringBuilder("<pi ");
193                             sb.Append(newReader.Value);
194                             sb.Append("/>");
195                             XmlTextReader piReader = new XmlTextReader(new StringReader(sb.ToString()));
196                             piReader.XmlResolver = null;
197                             piReader.DtdProcessing = DtdProcessing.Prohibit;
198                             piReader.Read();
199                             string type = piReader["type"];
200                             string alternate = piReader["alternate"];
201                             string href = piReader["href"];
202                             if (type != null && ContentType.MatchesBase(type, ContentType.TextXml)
203                                 && alternate != null && string.Compare(alternate, "yes", StringComparison.OrdinalIgnoreCase) == 0
204                                 && href != null) {
205                                 // we got a PI with the right attributes
206
207                                 // there is a link to a discovery document. follow it after fully qualifying it.
208                                 string newUrl = UriToString(Url, href);
209                                 document = GetDocumentNoParse(ref newUrl, ClientProtocol);
210                                 Url = newUrl;
211                                 break;
212                             }
213                         }
214                         newReader.Read();
215                     }
216                 }
217             }
218
219             if (document == null) {
220                 // there is no discovery document at this location
221                 Exception exception;
222                 if (ContentType.IsXml(contentType)) {
223                     exception = new ArgumentException(Res.GetString(Res.WebInvalidFormat));
224                 }
225                 else {
226                     exception = new InvalidContentTypeException(Res.GetString(Res.WebInvalidContentType, contentType), contentType);
227                 }
228                 throw new InvalidOperationException(Res.GetString(Res.WebMissingDocument, Url), exception);
229             }
230
231             ClientProtocol.References[Url] = this;
232             ClientProtocol.Documents[Url] = document;
233
234             foreach (object o in document.References) {
235                 if (o is DiscoveryReference) {
236                     DiscoveryReference r = (DiscoveryReference) o;
237                     if (r.Url.Length == 0) {
238                         throw new InvalidOperationException(Res.GetString(Res.WebEmptyRef, r.GetType().FullName, Url));
239                     }
240                     r.Url = UriToString(Url, r.Url);
241                     //All inheritors of DiscoveryReference that got URIs relative
242                     //to Ref property should adjust them like ContractReference does here.
243                     ContractReference cr = r as ContractReference;
244                     if ( (cr != null) && (cr.DocRef != null) ) {
245                         cr.DocRef = UriToString(Url, cr.DocRef);
246                     }
247                     r.ClientProtocol = ClientProtocol;
248                     ClientProtocol.References[r.Url] = r;
249                 }
250                 else
251                     ClientProtocol.AdditionalInformation.Add(o);
252             }
253
254             return;
255         }
256
257         /// <include file='doc\DiscoveryDocumentReference.uex' path='docs/doc[@for="DiscoveryDocumentReference.ResolveAll"]/*' />
258         /// <devdoc>
259         ///    <para>[To be supplied.]</para>
260         /// </devdoc>
261         public void ResolveAll() {
262             ResolveAll(true);
263         }
264
265         internal void ResolveAll(bool throwOnError) {
266             try {
267                 Resolve();
268             }
269             catch (Exception e) {
270                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
271                     throw;
272                 }
273                 if (throwOnError)
274                     throw;
275
276                 if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "ResolveAll", e);
277
278                 // can't continue, because we couldn't find a document.
279                 return;
280             }
281
282             foreach (object o in Document.References) {
283                 DiscoveryDocumentReference r = o as DiscoveryDocumentReference;
284                 if (r == null)
285                     continue;
286                 if (ClientProtocol.Documents[r.Url] != null) {
287                     continue;
288                 }
289                 r.ClientProtocol = ClientProtocol;
290                 r.ResolveAll(throwOnError);
291             }
292         }
293     }
294 }