Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

MapPath implementation is likely busted on IIS/IISExpress #222

Closed
davidfowl opened this issue Apr 1, 2015 · 19 comments
Closed

MapPath implementation is likely busted on IIS/IISExpress #222

davidfowl opened this issue Apr 1, 2015 · 19 comments
Assignees
Milestone

Comments

@davidfowl
Copy link
Member

MapPath ideally would respect the IIS VDir feature. As such, it needs to use IFileInfo.PhysicalPath to get the mapped path.

Helios would replace the IFileProvider with one that understood IIS paths.

@davidfowl
Copy link
Member Author

/cc @Praburaj

@Praburaj Praburaj self-assigned this Apr 1, 2015
@davidfowl davidfowl added this to the 1.0.0-beta5 milestone Apr 9, 2015
@Praburaj Praburaj removed their assignment Apr 21, 2015
@RickyLin
Copy link

which issue was this issue duplicated with?

@davidfowl davidfowl reopened this Jun 16, 2015
@davidfowl davidfowl reopened this Jun 16, 2015
@davidfowl
Copy link
Member Author

@Tratcher This method still needs to be fixed.

@davidfowl
Copy link
Member Author

Moved to beta6 /cc @muratg @Tratcher

@Tratcher
Copy link
Member

This is being tracked in a private repo for IIS. https://github.com/aspnet/Helios/issues/72

@davidfowl davidfowl reopened this Jun 16, 2015
@davidfowl
Copy link
Member Author

This needs to be kept open because the implementation of IHostingEnvironment.MapPath is incorrect. Even after the Helios bug is fixed this needs to be fixed still.

@Tratcher Tratcher assigned moozzyk and unassigned Tratcher Jun 16, 2015
@rubenprins
Copy link

@davidfowl, Please note that Server.MapPath is much smarter that many people realize, and we should have its functionalities available, since if the hosting framework (Helios) does not provide such information, it is 100% impossible to emulate this.

Take the following website structure:

web path physical path
/ d:\Inetpub\wwwroot
/myapp d:\Inetpub\myapp\wwwroot
/otherapp d:\Inetpub\otherapp\wwwroot

Then, with ASP.NET vCurrent (and largely ASP Classic too), from within a request path /myapp/path/ you'll get the following Server.MapPath results:

input output remarks
~/ d:\Inetpub\myapp\wwwroot application roots are a thing (in ASP.NET, not ASP Classic)
~/foo d:\Inetpub\myapp\wwwroot\foo foo does not have to exist!
foo d:\Inetpub\myapp\wwwroot\path\foo relative paths are resolved relative to the current request path!
/myapp/foo d:\Inetpub\myapp\wwwroot\foo this should work too
/ d:\Inetpub\wwwroot Server.MapPath can look outside the application!
/otherapp/foo d:\Inetpub\otherapp\foo

Note that the current implementation of MapPath doesn't handle any of the paths above, as it requires a path relative to the application's wwwroot folder (e.g., foo/bar, not even ~/foo/bar works properly!).

@davidfowl
Copy link
Member Author

Sure. We're going to implement something that makes sense given the abstraction we currently have. Exposing the full IIS capability would likely be another IIS specific interface (and helios does expose this), but it would require a dependency on that specific interface.

@rubenprins
Copy link

Even with Helios' specific IIS interface (I think you're referring to IHttpApplication - it's not actually documented anywhere) most path translations above are still not supported. It simply appends the relative path to the application's base path, just like HostingEnvironmentExtensions does, and only then it does its magic. This means it's impossible to MapPath outside the application (such as looking up parent and sibling paths). And the traditional ~/ paths are also mishandled.

And because of ASP.NET's wwwroot/approot model requiring child applications to use virtual directories pointing at folders outside the website's physical folder hierarchy to work, it's not even possible to find the parent website's physical wwwroot folder this way.

This means that even with said interface, we still cannot do what Server.MapPath does.

(I would have filed an issue on this to the Helios project, but you don't allow us to participate there. I foresee a future full of private reflection hacking answers on Stackoverflow just to get at the missing or incomplete features...)

@davidfowl
Copy link
Member Author

Even with Helios' specific IIS interface (I think you're referring to IHttpApplication - it's not actually documented anywhere) most path translations above are still not supported.

I actually haven't tried this interface myself but yes is it the IHttpApplication and it calls directly back into System.Web/IIS.

It simply appends the relative path to the application's base path, just like HostingEnvironmentExtensions does, and only then it does its magic. This means it's impossible to MapPath outside the application (such as looking up parent and sibling paths).

I'm guessing you tried it and it failed? Can you explain the scenario here? I know you're hosting a bunch of existing applications inside the same application pool based on an earlier discussion we have. I'd like to know how you're using configuring multiple sub applications within the same site (it'll be good for our knowledge so we can know how much or how little to expose).

And the traditional ~/ paths are also mishandled.

Yep, nothing is really handling ~/ paths the way VPPs did before.

(I would have filed an issue on this to the Helios project, but you don't allow us to participate there. I foresee a future full of private reflection hacking answers on Stackoverflow just to get at the missing or incomplete features...)

There's no need for that in the long term. We're going to make everything open source and it's unfortunate that Helios isn't yet (same for web listener).

It might be worth looking into how much functionality is reasonable to do in a non Helios specific way (via IFileProvider) and then look into a very IIS specific interface based on the use cases (and not just everything that worked before in ASP.NET v4).

@rubenprins
Copy link

Most of our applications are independent of each other, but they do share some configuration data. And some applications have multiple instances with exactly the same codebase + config files, but a different identity (and therefore different config anyway).

Sharing an application pool just makes managing applications easier, reduces memory pressure, and that's about it. Nothing architectural from the applications' perspective.

As far as the applications' configuration/identity, we have the following levels (mostly orthogonal):

  1. machine wide settings (thinks like machine key, default encryption settings, ASP.NET global switches),
  2. web-site specific settings (via inherited appSettings),
  3. app instance settings: a home grown system that provides app-specific (dynamic) config data and hosting permissions (availability, off switch, referrer logging/blocking/whitelisting, notification banners) that can be administered via an internal maintenance app, without the need to redeploy,
  4. app specific settings (used to be Web.config),
  5. site-wide HTTP modules (GAC'ed), optional and deployed per environment (e.g., only Staging servers).

(Next to what ASP.NET 5 would call environments, like Dev, Staging, Production.)

So /basepath/to/app1, /basepath/to/app2 and /to/app3 can be entirely different codebases, or they could be the same application, but with different settings (applied automatically), or shared settings, whatever is needed. For our hosting infrastructure it doesn't matter: you plop down an application with the right nuget packages, and it configures itself - automatically hooking up to the required middleware and services. With many applications, such automatic-and-default-is-correct instancing is important for us, because before we had our tooling, it was hit and miss whether the applications were configured properly. So now it's no more than a package update, and you've got the latest and correct middleware settings.

Now, for 2. and 3. we require at the very least the notion of a site above an application. This layer is completely missing both in the ASP.NET 5 Hosting infrastructure and the Helios interfaces (the IIS site name at the very least, but the IIS siteId and a working MapPath would be nice too; the siteId can be useful for correlating IIS logs with application logs).

Only a host name does not work for us, as multiple bindings are possible for sites, and background tasks (such as for prefetching/caching) have no host name. Environments also don't work, as they are orthogonal concepts: (Production, Staging, Development) × (site1, site2, etc.) × (hostname1, hostname2, etc.)

Given the current restrictions, we could have worked around site-specific settings, if we could read a config file common to all applications within a website (like /Web.config 😇). Which is what I started researching next.

But since neither ASP.NET 5 Hosting nor Helios have a working MapPath facility (form our perspective), and ASP.NET 5 child applications are required not to live in any folder hierarchy (not even the website's own root folder), while ASP.NET pre 5 applications do live in a true folder hierarchy that mirrors they URLs ... I cannot see how we can prevent duplicate configuration information all over the place. Unless we could traverse the virtual folder hierarchy outside the application's root path that is (like, say, /). It's going to be fragile. It's going to be a mess.

(Another solution could be the use of config transforms per publish profile, but as far as I can tell that's been thrown out as well. I don't see how this would be supported with .json files anyway, and Web.config/appSettings is now off-limits.)

Another reason to want to look beyond the current application, is when applications need to share settings or files. It is currently possible (pre 5) to use a shared folder on the site (with the proper IIS folder permissions). In that case, you could use Server.MapPath("/commonsettings/settings.config"), rather than hard coding physical paths. I don't like using relative paths like "../commonsettings/settings.config" and hope that this will work; and it wouldn't work when mixing ASP.NET 5 and pre 5 anyway, unless we restructure the pre 5 apps to live outside the site root like 5 apps, when we really dislike the 5 structure in the first place, and pray we have additional options in the future for 5.

Mind you, the points above would still require a major redesign for us to be able to get our infrastructure to work even at the most basic level on ASP.NET 5, with the complete absence of anything similar to WebActivatorEx/PreApplicationStartCode and HTTP Modules, which currently form our applications' backbone (no-code/no-config nuget packages providing required services/middleware).

And we're screwed with point 5 anyway (global HTTP modules), as ASP.NET 5 can't fully integrate with IIS (still need to test what happens, though, when try you mix the two). But luckily we never invested much time in such modules, so they could be replaced by middleware. Which we would need to copy-paste all over the place with some kind of detection to see if they're actually required. And unfortunately installing the modules in all individual applications rather than the specific servers/environments where they're actually needed.

(Can you tell I'm still not sold on the new all or nothing hand coded don't forget this special UseXyz() right after that UseAbc() [but not before] and do call UseServiceDEF() middleware pipeline? 😰)

@davidfowl
Copy link
Member Author

@rubenprins Thanks for that detailed explanation.

Another reason to want to look beyond the current application, is when applications need to share settings or files. It is currently possible (pre 5) to use a shared folder on the site (with the proper IIS folder permissions). In that case, you could use Server.MapPath("/commonsettings/settings.config"), rather than hard coding physical paths. I don't like using relative paths like "../commonsettings/settings.config" and hope that this will work; and it wouldn't work when mixing ASP.NET 5 and pre 5 anyway, unless we restructure the pre 5 apps to live outside the site root like 5 apps, when we really dislike the 5 structure in the first place, and pray we have additional options in the future for 5.

Why do you even need MapPath for this? What are virtual directories buying you in this case? Do you ever want to share website content? Or are you mostly just using this as a way to share data/settings/code between sites?

Mind you, the points above would still require a major redesign for us to be able to get our infrastructure to work even at the most basic level on ASP.NET 5, with the complete absence of anything similar to WebActivatorEx/PreApplicationStartCode and HTTP Modules, which currently form our applications' backbone (no-code/no-config nuget packages providing required services/middleware).

So far we're completely avoiding anything like this but it should be possible to build a system like this yourself with the new stack. It might be good to write up a sample and possible make something like this an optional thing for people that want to use it. It'll also help us validate if there are any huge gaps to enabling something like this but it won't be in the box.

@rubenprins
Copy link

@davidfowl, I feel like we're not getting closer to a common understanding here.

Why do you even need MapPath for this? What are virtual directories buying you in this case? Do you ever want to share website content? Or are you mostly just using this as a way to share data/settings/code between sites?

Not sites, but applications within a site (singular). At the moment site and application appear to be conflated a lot. I'm specifically talking about IIS child applications of sites. As in http://mysite/appA and http://mysite/appB, but NOT http://myothersite/appC, all on the same IIS server.

(Currently, ASP.NET 5 has no notion of sites, only applications; we also need something like HostingEnvironment.SiteName. But that's not directly related to MapPath, just another problem to tackle.)

As it stands, the new MapPath cannot look outside its own application folder: letting /customers/contoso/some-app/ look at /customers/contoso/, /, or even /customers/contoso/convoluted/thingy.

This can — just an example — be used to (re)build configuration inheritance. (Web.config inheritance might have been a pain for you to maintain from an implementation point of view, but in specific scenarios it does have its merits.) It can also be used to share data. It can be used for file checks on an adjacent virtual directory containing only static files. It can be (ab)used for just about anything.

ASP Classic and ASP.NET vCurrent can do this right now. And v5 has no way to even emulate it.

@davidfowl
Copy link
Member Author

@rubenprins Oh no I think I understand your scenario. I shouldn't have said site, I actually meant application. You're right, MapPath doesn't work the way you want it to because we separate code from the wwwroot, but I'm not convinced you can't make it work any other way. We specifically don't allow looking outside of the application folder but we'll definitely take some under consideration as it's a pretty interesting scenario.

In the future we can look at ways to expose the lower level layers of IIS to see something like this can be added so that this might be in the realm of possibility.

@rubenprins
Copy link

@davidfowl, Well, I still don't think we're on the same page here, with the new MapPath treating / as the application's wwwroot, rather than the physical path corresponding to the path /. I fear you have painted yourself (and us) in a corner here, as there is no way you'll later be able to support the full range of virtual paths without breaking everyting. I still maintain ~/ is fundamentally different from /, but alas.

Also, this is not an IIS feature. Apache and other real world web servers support the concept of virtual paths. I firmly believe you're too fixated on tiny node.js-like hosts which can only handle a single application, and fixing ASP.NET 5 on the lowest common denominator because of that.

And it's not like IHttpApplication can be consumed via DI (or any way else) anyway, so I'll see what the future will bring. Let's hope we don't need to wait for v6 for such basic hosting features, because we're architecturally stuck right now with the lack of what you call "low level" IIS features, for which there is only one replacement: ASP.NET 4.

@davidfowl
Copy link
Member Author

If you want to merge your wwwroot and approoot back into a single folder you can. I don't recommend it but it'll enable what you want to do. We don't have any native support for ~/ vs / and no, I don't think we've painted ourselves into a corner, the design was intentional and we do need to work xplatform and in more places than before. There's probably even a way to achieve what you want without those features but it seems you're fixated on making it worked the way it always has.

This is an IIS and Apache specific feature then 😀 but we don't have an apache plugin so it's less relevant.

I don't want to pollute this bug anymore, sounds like you have a general problem with the direction of the whole product. Maybe open up an issue on aspnet/Home because this isn't the right place for that discussion.

@davidfowl
Copy link
Member Author

Closing this out as we're not going to support IIS MapPath. @rubenprins I know this will not work with how you are currently deploying your ASP.NET 4.x apps but it's something we aren't brining back (at least as it stands right now).

@rubenprins
Copy link

@davidfowl, Since IIS integration (via Helios) will not be released, MapPath support will be the least of the existing ASP.NET user base's hosting & deployment concerns.

As for us, we're still running IIS 7.5 servers (I know 😒), so the suggested "alternative" to Helios – HttpPlatformHandler – is not supported. This pretty much means that ASP.NET 5 is no longer a viable platform for us, for the foreseeable future anyway.

@davidfowl
Copy link
Member Author

We're actually making http platform handler work down level

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants