New test.
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Discovery / DiscoveryClientProtocol.cs
1 // \r
2 // System.Web.Services.Protocols.DiscoveryClientProtocol.cs\r
3 //\r
4 // Author:\r
5 //   Dave Bettin (javabettin@yahoo.com)\r
6 //   Lluis Sanchez Gual (lluis@ximian.com)\r
7 //\r
8 // Copyright (C) Dave Bettin, 2002\r
9 //\r
10 \r
11 //\r
12 // Permission is hereby granted, free of charge, to any person obtaining\r
13 // a copy of this software and associated documentation files (the\r
14 // "Software"), to deal in the Software without restriction, including\r
15 // without limitation the rights to use, copy, modify, merge, publish,\r
16 // distribute, sublicense, and/or sell copies of the Software, and to\r
17 // permit persons to whom the Software is furnished to do so, subject to\r
18 // the following conditions:\r
19 // \r
20 // The above copyright notice and this permission notice shall be\r
21 // included in all copies or substantial portions of the Software.\r
22 // \r
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
30 //\r
31 \r
32 using System.Collections;\r
33 using System.IO;\r
34 using System.Web.Services.Protocols;\r
35 using System.Web.Services.Description;\r
36 using System.Xml;\r
37 using System.Xml.Schema;\r
38 using System.Xml.Serialization;\r
39 using System.Net;\r
40 using System.Text.RegularExpressions;\r
41 \r
42 namespace System.Web.Services.Discovery {\r
43         public class DiscoveryClientProtocol : HttpWebClientProtocol {\r
44 \r
45                 #region Fields\r
46 \r
47                 private IList additionalInformation = new ArrayList ();\r
48                 private DiscoveryClientDocumentCollection documents = new DiscoveryClientDocumentCollection();\r
49                 private DiscoveryExceptionDictionary errors = new DiscoveryExceptionDictionary();\r
50                 private DiscoveryClientReferenceCollection references = new DiscoveryClientReferenceCollection();\r
51 \r
52                 #endregion // Fields\r
53 \r
54                 #region Constructors\r
55 \r
56                 public DiscoveryClientProtocol () \r
57                 {\r
58                 }\r
59                 \r
60                 #endregion // Constructors\r
61 \r
62                 #region Properties\r
63 \r
64                 public IList AdditionalInformation {\r
65                         get { return additionalInformation; }\r
66                 }\r
67                 \r
68                 public DiscoveryClientDocumentCollection Documents {\r
69                         get { return documents; }\r
70                 }\r
71                 \r
72                 public DiscoveryExceptionDictionary Errors {\r
73                         get { return errors; }\r
74                 }\r
75 \r
76                 public DiscoveryClientReferenceCollection References {\r
77                         get { return references; }\r
78                 }               \r
79                 \r
80                 #endregion // Properties\r
81 \r
82                 #region Methods\r
83 \r
84                 public DiscoveryDocument Discover (string url)\r
85                 {\r
86                         Stream stream = Download (ref url);\r
87                         XmlTextReader reader = new XmlTextReader (url, stream);\r
88                         if (!DiscoveryDocument.CanRead (reader)) \r
89                                 throw new InvalidOperationException ("The url '" + url + "' does not point to a valid discovery document");\r
90                                 \r
91                         DiscoveryDocument doc = DiscoveryDocument.Read (reader);\r
92                         reader.Close ();\r
93                         documents.Add (url, doc);\r
94                         AddDiscoReferences (doc);\r
95                         return doc;\r
96                 }\r
97 \r
98                 public DiscoveryDocument DiscoverAny (string url)\r
99                 {\r
100                         try\r
101                         {\r
102                                 string contentType = null;\r
103                                 Stream stream = Download (ref url, ref contentType);\r
104         \r
105                                 if (contentType.IndexOf ("text/html") != -1)\r
106                                 {\r
107                                         // Look for an alternate url\r
108                                         \r
109                                         StreamReader sr = new StreamReader (stream);\r
110                                         string str = sr.ReadToEnd ();\r
111                                         \r
112                                         string rex = "link\\s*rel\\s*=\\s*[\"']?alternate[\"']?\\s*";\r
113                                         rex += "type\\s*=\\s*[\"']?text/xml[\"']?\\s*href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|'(?<1>[^']*)'|(?<1>\\S+))";\r
114                                         Regex rob = new Regex (rex, RegexOptions.IgnoreCase);\r
115                                         Match m = rob.Match (str);\r
116                                         if (!m.Success) \r
117                                                 throw new InvalidOperationException ("The HTML document does not contain Web service discovery information");\r
118                                         \r
119                                         if (url.StartsWith ("/"))\r
120                                         {\r
121                                                 Uri uri = new Uri (url);\r
122                                                 url = uri.GetLeftPart (UriPartial.Authority) + m.Groups[1];\r
123                                         }\r
124                                         else\r
125                                         {\r
126                                                 int i = url.LastIndexOf ('/');\r
127                                                 if (i == -1)\r
128                                                         throw new InvalidOperationException ("The HTML document does not contain Web service discovery information");\r
129 \r
130                                                 Uri tmp = new Uri (url);\r
131                                                 tmp = new Uri (tmp, m.Groups [1].ToString ());\r
132                                                 url = tmp.ToString ();\r
133                                         }\r
134                                         stream = Download (ref url);\r
135                                 }\r
136                                 \r
137                                 XmlTextReader reader = new XmlTextReader (url, stream);\r
138                                 reader.MoveToContent ();\r
139                                 DiscoveryDocument doc;\r
140                                 DiscoveryReference refe = null;\r
141                                 \r
142                                 if (DiscoveryDocument.CanRead (reader))\r
143                                 {\r
144                                         doc = DiscoveryDocument.Read (reader);\r
145                                         documents.Add (url, doc);\r
146                                         refe = new DiscoveryDocumentReference ();\r
147                                         AddDiscoReferences (doc);\r
148                                 }\r
149                                 else if (ServiceDescription.CanRead (reader))\r
150                                 {\r
151                                         ServiceDescription wsdl = ServiceDescription.Read (reader);\r
152                                         documents.Add (url, wsdl);\r
153                                         doc = new DiscoveryDocument ();\r
154                                         refe = new ContractReference ();\r
155                                         doc.References.Add (refe);\r
156                                         refe.Url = url;\r
157                                         ((ContractReference)refe).ResolveInternal (this, wsdl);\r
158                                 }\r
159                                 else\r
160                                 {\r
161                                         XmlSchema schema = XmlSchema.Read (reader, null);\r
162                                         documents.Add (url, schema);\r
163                                         doc = new DiscoveryDocument ();\r
164                                         refe = new SchemaReference ();\r
165                                         doc.References.Add (refe);\r
166                                 }\r
167                                 \r
168                                 refe.ClientProtocol = this;\r
169                                 refe.Url = url;\r
170                                 references.Add (url, refe);\r
171                                         \r
172                                 reader.Close ();\r
173                                 return doc;\r
174                         }\r
175                         catch (DiscoveryException ex) {\r
176                                 throw ex.Exception;\r
177                         }\r
178                 }\r
179                 \r
180                 void AddDiscoReferences (DiscoveryDocument doc)\r
181                 {\r
182                         foreach (DiscoveryReference re in doc.References)\r
183                         {\r
184                                 re.ClientProtocol = this;\r
185                                 references.Add (re.Url, re);\r
186                         }\r
187                         \r
188                         if (doc.AdditionalInfo != null) {\r
189                                 foreach (object info in doc.AdditionalInfo)\r
190                                         additionalInformation.Add (info);\r
191                         }\r
192                 }\r
193                 \r
194                 public Stream Download (ref string url)\r
195                 {\r
196                         string contentType = null;\r
197                         return Download (ref url, ref contentType);\r
198                 }\r
199                 \r
200                 public Stream Download (ref string url, ref string contentType)\r
201                 {\r
202                         if (url.StartsWith ("http://") || url.StartsWith ("https://"))\r
203                         {\r
204                                 WebRequest request = GetWebRequest (new Uri(url));\r
205                                 WebResponse resp = request.GetResponse ();\r
206                                 contentType = resp.ContentType;\r
207                                 return resp.GetResponseStream ();\r
208                         }\r
209                         else if (url.StartsWith ("file://"))\r
210                         {\r
211                                 WebRequest request = WebRequest.Create (new Uri (url));\r
212                                 WebResponse resp = request.GetResponse ();\r
213                                 contentType = resp.ContentType;\r
214                                 return resp.GetResponseStream ();\r
215                         }\r
216                         else\r
217                         {\r
218                                 string ext = Path.GetExtension (url).ToLower();\r
219                                 if (ext == ".wsdl" || ext == ".xsd")\r
220                                 {\r
221                                         contentType = "text/xml";\r
222                                         return new FileStream (url, FileMode.Open, FileAccess.Read);\r
223                                 }\r
224                                 else\r
225                                         throw new InvalidOperationException ("Unrecognized file type '" + url + "'. Extension must be one of .wsdl or .xsd");\r
226                         }\r
227                 }\r
228 \r
229                 [Obsolete ("This method will be removed from a future version. The method call is no longer required for resource discovery", false)]\r
230                 public void LoadExternals ()\r
231                 {\r
232                 }\r
233 \r
234                 public DiscoveryClientResultCollection ReadAll (string topLevelFilename)\r
235                 {\r
236                         StreamReader sr = new StreamReader (topLevelFilename);\r
237                         XmlSerializer ser = new XmlSerializer (typeof (DiscoveryClientResultsFile));\r
238                         DiscoveryClientResultsFile resfile = (DiscoveryClientResultsFile) ser.Deserialize (sr);\r
239                         sr.Close ();
240                         
241                         string basePath = Path.GetDirectoryName (topLevelFilename);\r
242                         \r
243                         foreach (DiscoveryClientResult dcr in resfile.Results)\r
244                         {\r
245                                 Type type = Type.GetType (dcr.ReferenceTypeName);\r
246                                 DiscoveryReference dr = (DiscoveryReference) Activator.CreateInstance (type);\r
247                                 dr.Url = dcr.Url;\r
248                                 FileStream fs = new FileStream (Path.Combine (basePath, dcr.Filename), FileMode.Open, FileAccess.Read);\r
249                                 Documents.Add (dr.Url, dr.ReadDocument (fs));\r
250                                 fs.Close ();\r
251                                 References.Add (dr.Url, dr);\r
252                         }\r
253                         return resfile.Results;\r
254                 }\r
255 \r
256                 public void ResolveAll ()\r
257                 {\r
258                         ArrayList list = new ArrayList (References.Values);\r
259                         foreach (DiscoveryReference re in list)\r
260                         {\r
261                                 try\r
262                                 {\r
263                                         if (re is DiscoveryDocumentReference)\r
264                                                 ((DiscoveryDocumentReference)re).ResolveAll ();\r
265                                         else\r
266                                                 re.Resolve ();\r
267                                 }\r
268                                 catch (DiscoveryException ex)\r
269                                 {\r
270                                         Errors [ex.Url] = ex.Exception; \r
271                                 }\r
272                                 catch (Exception ex)\r
273                                 {\r
274                                         Errors [re.Url] = ex;   \r
275                                 }\r
276                         }\r
277                 }\r
278                 \r
279                 public void ResolveOneLevel ()\r
280                 {\r
281                         ArrayList list = new ArrayList (References.Values);\r
282                         foreach (DiscoveryReference re in list)\r
283                                 re.Resolve ();\r
284                 }\r
285                 \r
286                 public DiscoveryClientResultCollection WriteAll (string directory, string topLevelFilename)\r
287                 {\r
288                         DiscoveryClientResultsFile resfile = new DiscoveryClientResultsFile();\r
289                         \r
290                         foreach (DiscoveryReference re in References.Values)\r
291                         {\r
292                                 object doc = Documents [re.Url];\r
293                                 if (doc == null) continue;\r
294                                 \r
295                                 string fileName = FindValidName (resfile, re.DefaultFilename);\r
296                                 resfile.Results.Add (new DiscoveryClientResult (re.GetType(), re.Url, fileName));\r
297                                 \r
298                                 string filepath = Path.Combine (directory, fileName);\r
299                                 FileStream fs = new FileStream (filepath, FileMode.Create, FileAccess.Write);\r
300                                 re.WriteDocument (doc, fs);\r
301                                 fs.Close ();\r
302                         }\r
303                         \r
304                         StreamWriter sw = new StreamWriter (Path.Combine (directory, topLevelFilename));\r
305                         XmlSerializer ser = new XmlSerializer (typeof (DiscoveryClientResultsFile));\r
306                         ser.Serialize (sw, resfile);\r
307                         sw.Close ();\r
308                         return resfile.Results;\r
309                 }\r
310                 \r
311                 string FindValidName (DiscoveryClientResultsFile resfile, string baseName)\r
312                 {\r
313                         string name = baseName;\r
314                         int id = 0;\r
315                         bool found;\r
316                         do\r
317                         {\r
318                                 found = false;\r
319                                 foreach (DiscoveryClientResult res in resfile.Results)\r
320                                 {\r
321                                         if (name == res.Filename) {\r
322                                                 found = true; break;\r
323                                         }\r
324                                 }\r
325                                 if (found)\r
326                                         name = Path.GetFileNameWithoutExtension (baseName) + (++id) + Path.GetExtension (baseName);\r
327                         }\r
328                         while (found);\r
329                         \r
330                         return name;\r
331                 }\r
332                 \r
333                 #endregion // Methods\r
334                 \r
335                 #region Classes\r
336                 \r
337                 public sealed class DiscoveryClientResultsFile {\r
338                         \r
339                         #region Fields\r
340                         \r
341                         private DiscoveryClientResultCollection results;\r
342 \r
343                         #endregion // Fields\r
344 \r
345                         #region Contructors\r
346                         \r
347                         public DiscoveryClientResultsFile () \r
348                         {\r
349                                 results = new DiscoveryClientResultCollection ();\r
350                         }\r
351                 \r
352                         #endregion // Constructors\r
353                         \r
354                         #region Properties\r
355                 \r
356                         public DiscoveryClientResultCollection Results {                                \r
357                                 get { return results; }\r
358                         }\r
359                         \r
360                         #endregion // Properties\r
361                 }\r
362                 \r
363                 #endregion // Classes\r
364         }\r
365                 \r
366         internal class DiscoveryException : Exception\r
367         {\r
368                 public string Url;\r
369                 public Exception Exception;\r
370                 \r
371                 public DiscoveryException (string url, Exception origin)\r
372                 {\r
373                         Url = url;\r
374                         Exception = origin;\r
375                 }\r
376         }\r
377 }\r