Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web.Services / System / Web / Services / Discovery / DynamicVirtualDiscoSearcher.cs
blob863998aafd38019e5900c1c5734a27c90cf3c11c
1 //------------------------------------------------------------------------------
2 // <copyright file="DynamicVirtualDiscoSearcher.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 namespace System.Web.Services.Discovery {
8 using System;
9 using System.IO;
10 using System.Collections;
11 using System.Diagnostics;
12 using System.Text;
13 using System.DirectoryServices;
14 using System.ComponentModel;
15 using System.Globalization;
16 using System.Threading;
17 using System.Web.Services.Diagnostics;
19 /// <include file='doc\DynamicVirtualDiscoSearcher.uex' path='docs/doc[@for="DynamicVirtualDiscoSearcher"]/*' />
20 /// <devdoc>
21 /// Does a recursive search of virtual subdirectories to find stuff to
22 /// make a disco file from. *.disco files (or whatever the PrimarySearchPattern is) are
23 /// treated as end-points - recursion stops where they are found.
24 /// </devdoc>
25 internal class DynamicVirtualDiscoSearcher : DynamicDiscoSearcher {
27 private string rootPathAsdi; // ADSI search root path with prefix
28 private string entryPathPrefix;
30 private string startDir;
32 // If we could get an event back from IIS Admin Object that some directory is addred/removed/renamed
33 // then the following memeber should become static, so we get 5-10 _times_ performace gain on
34 // processing .vsdisco in the Web Root
35 // !!SEE ALSO!! CleanupCache method
36 private /*static*/ Hashtable webApps = new Hashtable();
37 private Hashtable Adsi = new Hashtable();
40 // -------------------------------------------------------------------------------
41 internal DynamicVirtualDiscoSearcher(string startDir, string[] excludedUrls, string rootUrl) :
42 base(excludedUrls)
44 origUrl = rootUrl;
45 entryPathPrefix = GetWebServerForUrl( rootUrl ) + "/ROOT";
47 this.startDir = startDir;
49 string localPath = (new System.Uri(rootUrl)).LocalPath;
50 if ( localPath.Equals("/") ) localPath = ""; // empty local path should be ""
51 rootPathAsdi = entryPathPrefix + localPath;
54 // -------------------------------------------------------------------------------
55 /// <include file='doc\DynamicVirtualDiscoSearcher.uex' path='docs/doc[@for="DynamicVirtualDiscoSearcher.Search"]/*' />
56 /// <devdoc>
57 /// Main function. Searches dir recursively for primary (.vsdisco) and seconary (.asmx) files.
58 /// </devdoc>
59 internal override void Search(string fileToSkipAtBegin) {
60 SearchInit(fileToSkipAtBegin);
61 ScanDirectory( rootPathAsdi );
62 CleanupCache();
65 // -------------------------------------------------------------------------------
66 // Look in virtual subdirectories.
67 protected override void SearchSubDirectories(string nameAdsiDir) {
69 if ( CompModSwitches.DynamicDiscoverySearcher.TraceVerbose ) Debug.WriteLine( "DynamicVirtualDiscoSearcher.SearchSubDirectories(): nameAdsiDir=" + nameAdsiDir);
71 DirectoryEntry vdir = (DirectoryEntry)Adsi[nameAdsiDir]; //may be already bound
72 if (vdir == null) {
73 if ( !DirectoryEntry.Exists(nameAdsiDir) )
74 return;
75 vdir = new DirectoryEntry(nameAdsiDir);
76 Adsi[nameAdsiDir] = vdir;
79 foreach (DirectoryEntry obj in vdir.Children) {
80 DirectoryEntry child = (DirectoryEntry)Adsi[obj.Path];
81 if (child == null) {
82 child = obj;
83 Adsi[obj.Path] = obj;
84 } else {
85 obj.Dispose();
87 AppSettings settings = GetAppSettings(child);
88 if (settings != null) {
89 ScanDirectory(child.Path); //go down ADSI path
95 // -------------------------------------------------------------------------------
96 protected override DirectoryInfo GetPhysicalDir(string dir ) {
97 DirectoryEntry vdir = (DirectoryEntry)Adsi[dir];
98 if (vdir == null) {
99 if (!DirectoryEntry.Exists(dir) ) {
100 return null;
102 vdir = new DirectoryEntry(dir);
103 Adsi[dir] = vdir;
105 try {
106 DirectoryInfo directory = null;
107 AppSettings settings = GetAppSettings(vdir);
108 if (settings == null) {
109 return null;
111 if (settings.VPath == null) { //SchemaClassName == "IIsWebDirectory"
112 //NOTE This assumes there was a known physical directory
113 //corresponding to a parent WebDirectory.
114 //And incoming 'dir' is a child of that parent.
115 if ( !dir.StartsWith(rootPathAsdi, StringComparison.Ordinal) ) {
116 throw new ArgumentException(Res.GetString(Res.WebVirtualDisoRoot, dir, rootPathAsdi), "dir");
118 string physicalDir = dir.Substring(rootPathAsdi.Length);
119 physicalDir = physicalDir.Replace('/', '\\'); //it always begins with '/' or is empty
120 directory = new DirectoryInfo(startDir + physicalDir);
123 else {
124 directory = new DirectoryInfo(settings.VPath); //SchemaClassName == "IIsWebVirtualDir
127 if ( directory.Exists )
128 return directory;
130 catch (Exception e) {
131 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
132 throw;
134 if ( CompModSwitches.DynamicDiscoverySearcher.TraceVerbose ) Debug.WriteLine( "+++ DynamicVirtualDiscoSearcher.GetPhysicalDir(): dir=" + dir + " Exception=" + e.ToString() );
135 if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "GetPhysicalDir", e);
136 return null;
138 return null;
142 // -------------------------------------------------------------------------------
143 // Calculate root ADSI virtual directory name (func by 'Microsoft').
144 private string GetWebServerForUrl(string url) {
145 Uri uri = new Uri(url);
146 DirectoryEntry w3Service = new DirectoryEntry("IIS://" + uri.Host + "/W3SVC");
148 foreach (DirectoryEntry obj in w3Service.Children) {
149 DirectoryEntry site = (DirectoryEntry)Adsi[obj.Path]; //may be already bound
150 if (site == null) {
151 site = obj;
152 Adsi[obj.Path] = obj;
154 else {
155 obj.Dispose();
157 AppSettings settings = GetAppSettings(site);
159 if (settings == null || settings.Bindings == null) { //SchemaClassName != "IIsWebServer"
160 continue;
163 foreach (string bindingsEntry in settings.Bindings) {
164 if ( CompModSwitches.DynamicDiscoverySearcher.TraceVerbose ) Debug.WriteLine("GetWebServerForUrl() bindingsEntry=" + bindingsEntry);
165 string[] bindings = bindingsEntry.Split(':');
166 string ip = bindings[0];
167 string port = bindings[1];
168 string hostname = bindings[2];
170 if (Convert.ToInt32(port, CultureInfo.InvariantCulture) != uri.Port)
171 continue;
173 if (uri.HostNameType == UriHostNameType.Dns) {
174 if (hostname.Length == 0 || string.Compare(hostname, uri.Host, StringComparison.OrdinalIgnoreCase) == 0)
175 return site.Path;
177 else {
178 if (ip.Length == 0 || string.Compare(ip, uri.Host, StringComparison.OrdinalIgnoreCase) == 0)
179 return site.Path;
183 return null;
186 // -------------------------------------------------------------------------------
187 // Makes result URL found file path from diectory name and short file name.
188 protected override string MakeResultPath(string dirName, string fileName) {
189 string res = origUrl
190 + dirName.Substring(rootPathAsdi.Length, dirName.Length - rootPathAsdi.Length)
191 + '/' + fileName;
192 return res;
195 // -------------------------------------------------------------------------------
196 // Makes exclusion path absolute for quick comparision on search.
197 protected override string MakeAbsExcludedPath(string pathRelativ) {
198 return rootPathAsdi + '/' + pathRelativ.Replace('\\', '/');
201 // -------------------------------------------------------------------------------
202 protected override bool IsVirtualSearch {
203 get { return true; }
206 private AppSettings GetAppSettings(DirectoryEntry entry) {
207 string key = entry.Path; //this is fast since does not cause bind()
208 AppSettings result = null;
210 object obj = webApps[key];
212 if (obj == null) {
213 // We provie a write lock while Hashtable supports multiple readers under single writer
214 lock (webApps) {
215 obj = webApps[key];
216 if (obj == null) { //make sure other thread not taken care of
217 result = new AppSettings(entry); //that consumes a 50-2000 ms
218 webApps[key] = result;
222 else {
223 result = (AppSettings)obj;
225 return result.AccessRead ? result : null; //ignore denied object on upper level
228 private void CleanupCache() {
229 //Destroy system resources excplicitly since the destructor is called sometime late
230 foreach (DictionaryEntry obj in Adsi) {
231 ((DirectoryEntry)(obj.Value)).Dispose();
233 rootPathAsdi = null;
234 entryPathPrefix = null;
235 startDir = null;
237 Adsi = null;
238 //REMOVE NEXT LINE IF the member webApps has turned into static (see webApps declaration line)
239 webApps = null;
242 private class AppSettings {
243 internal readonly bool AccessRead = false; // if false the caller will ignore the object
244 internal readonly string[] Bindings = null; // the field is only for WebServers
245 internal readonly string VPath = null; // the filed is only for VirtualDirs
247 internal AppSettings(DirectoryEntry entry) {
249 string schema = entry.SchemaClassName;
250 AccessRead = true;
252 if (schema == "IIsWebVirtualDir" || schema == "IIsWebDirectory") {
253 if (!(bool)(entry.Properties["AccessRead"][0])) {
254 AccessRead = false;
255 return;
257 if (schema == "IIsWebVirtualDir") {
258 VPath = (string) (entry.Properties["Path"][0]);
261 else if (schema == "IIsWebServer") {
262 Bindings = new string[entry.Properties["ServerBindings"].Count];
263 for (int i = 0; i < Bindings.Length; ++i) {
264 Bindings[i] = (string) (entry.Properties["ServerBindings"][i]);
267 else {
268 //schema is not recognized add to the cache but never look at the object
269 AccessRead = false;