Asynchronous Streaming Reverse Proxy for IIS 6.0
I recently started to use Subsonic on my Windows Home Server to make my music collection available over the internet. Subsonic must be configured to run on a port other than port 80, because this port is already used by the IIS web server. This is a major inconvenience, because it may prevent you from accessing the media from firewall restricted networks, and it will make the URLs to your media server somewhat more ugly. In order to get around these two problems, IIS has to serve as a proxy for the server.
Overview
Unfortunately, IIS does not have built-in proxy functionality, and after looking at many possible solutions, including proxy servers (such as Squid), ISAPI filters (such as IIS Mod-Rewrite, IIS Proxy, ISAPI_Rewrite, IIRF and ManagedFusion Url Rewriter) and various ASP.NET scripts, I found that they either cost money, did not work for my purpose or had other disadvantages. One blog post even suggested the parallel installation of Apache with mod_proxy to provide the required functionality, but you may agree that installing a second web server for such a simple problem cannot be an elegant solution.
I came to the conclusion that the best workaround would be to write a custom HTTP handler DLL, which can easily be added to an existing IIS installation. Based on existing implementations on the internet, I wrote an asynchronous reverse proxy with support for content streaming and URL reversal. The HTTP handler has been written and tested with Subsonic, but it should be generic enough to work with many other types of HTTP based servers, including HTTPS and ASP.NET. I have also included the full source code and a sample configuration file for use with Subsonic.
Download
By downloading the files below you agree that the software is provided “as is”, without warranty of any kind, expressed or implied. In no event shall the author or its contributors be held liable for any direct, indirect, incidental, special or consequential damages arising out of the use of or inability to use this software.
Version: CL 94
Released: 2010/12/27
Binary: ReverseProxyHandler.zip (10.8 kB)
Source: ReverseProxyHandler_Source.zip (11.7 kB)
Prerequisites
The project has been compiled against .NET Framework version 4.0.30319. Make sure to install the latest version of the framework on your server. Alternatively, you can compile the included source code against an earlier version of the framework. Minor code modifications may be necessary. If there is sufficient interest in other frameworks and platforms, I may include additional pre-built binaries in future versions of the download.
Installation
The reverse proxy is delivered in the form of a dynamic link library (DLL), which will be executed through the ASP.NET ISAPI extension. The configuration options for the reverse proxy are in a web.config file. Both the DLL and the configuration file need to be installed in the following way:
- In IIS Manager, create a new website or a virtual directory in an existing website for the location you want to be proxied.
- In Windows Explorer, unpack the downloaded archive ReverseProxyHandler.zip into this location.
- In IIS Manager, open the Properties dialog for the location.
- Navigate to the Home Directory tab and click on the Configuration… button in the Application Settings section.
- In the Mappings tab, click the Insert… button in the Wildcard application maps section.
- Enter c:\windows\microsoft.net\framework\v4.0.30319\aspnet_isapi.dll
- Uncheck Verify that file exists
- Click OK on all dialog windows to return to the IIS Manager.
- Modify the file web.config to configure your specific setup.
At this point, all requests to the website or virtual directory should be forward to the remote server that you configured. The remote server’s replies will be sent back to IIS, which replaces all hard-coded remote URLs that may exist in them and then forwards it to the client.
In order to separate the execution of requests to the proxied website or virtual directory, you may want to create a special application pool and assign it in the Properties dialog of the website or virtual directory. On my own server, I created a ProxyAppPool for this purpose.
Configuration
All configuration options for the reverse proxy are included in the web.config file, which is placed in the root of each website or virtual directory to be proxied.
The following configuration options are available:
AllowRemoteCompression A value of true
enables support for gzip and deflate compression of the remote server’s response stream. All compressed data from the remote server will then automatically be decompressed in IIS prior to parsing it for URL reversal. A value of false
will notify the remote server that compressed response streams are not supported, in which case the remote server should send all data uncompressed. This may be useful if the remote server is on the same LAN or if proxy performance becomes an issue due to decompression overhead in IIS.
EventLogEnabled A value of true
will enable debug logs in the Windows Event Log (see next paragraph for additional instructions).
EventLogName Specifies the name of the event log the entries will be written to. The default is Application
EventLogSource Specifies the name of the event log source that generates the event log entries. The default is ReverseProxy
RemoteWebsite Specifies the full URL of the remote website that is the target of the proxy request.
For example, if you configured the proxy for a website with the URL http://www.yourdomain.com and the remote website is configured as http://localhost:123
, then a request of the form http://www.yourdomain.com/foldername/filename.txt will be resolved internally as http://localhost:123/foldername/filename.txt.
ReverseContentTypes Specifies the HTTP content types that need to be parsed for URLs. Some websites or web applications may contain hard-coded URLs in their response that point to the remote server. Since the remote server is proxied, these links will be dysfunctional from the client’s point of view.
For example, if the remote server returns an HTML page with an image http://localhost:123/images/pic.jpg and the text/html content type is included in this setting, the reverse proxy will replace the URL with http://www.yourdomain.com/images/pic.jpg before the HTML page is sent back to the client.
ReverseBasePrefixes Specifies a comma-separated list of text prefixes that will be parsed for reversal of base URLs. Base URLs start with a forward slash and are used by some web applications when they assume that they are running in the root of the application directory.
For example, a website may return an image with src=”/images/foo.jpg”
. If the proxy is running inside a virtual directory then this URL needs to be reversed to src=”/virtualdirectory/images/foo.jpg”
.
A prefix will be used to filter the response text in two ways: with a trailing double-quote (PREFIX”/
) and a trailing single-quote (PREFIX’/
). For example, the prefix src=`` will match
src=”/and `src=’/
.
Note that base URLs are sometimes generated at run-time in JavaScript. In such cases you will have to locate the offending script line and find an appropriate prefix. For example, in the Subsonic music server there is a JavaScript function call setupZoom(‘/’)
that specifies the base directory for images, which is why the prefix setupZoom(
has been added to the example web.config file.
Debugging
The reverse proxy is able to log HTTP requests and their results to the Windows Event Log. This will help you to determine if URLs are resolved correctly and which content types need to be checked for URL reversal. By default the event log is not accessible by ASP.NET, so that you may get a security permission error when logging is enabled. You can allow access to the event log by performing the following steps:
- Open the Registry Editor (regedit.exe)
- Navigate to the following key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application - Create a new sub-key called ReverseProxy
- Right-click this new key and select Permissions…
- Grant full control to the user account running the application pool for your web site (by default this is NETWORK SERVICE)
If you are not sure which user account is used by the application pool:
- Start IIS Manager
- Open Properties for your web site
- Select Home Directory tab
- Note the Application pool name at the bottom
- Back in IIS Manager, open the Properties for the application pool
- Select the Identity tab to view or change the selected user account.
Implementation Details
The reverse proxy is implemented as an asynchronous HTTP handler that inherits from IHttpAsyncHandler. The handler instance itself keeps no internal state and is therefore reused for subsequent requests to conserve memory and reduce memory fragmentation.
The DLL included in the download has been compiled with Any CPU as the target platform, so that it will run on both 32-bit and 64-bit servers. Feel free to compile a platform-specific version from the included source code.
Every request from the client browser will be forwarded to an instance of HttpWebRequest. All headers and other important fields are fully preserved. The remote request itself is executed asynchronously, and the state information is kept in an AsyncState object.
Please note that the handler is asynchronous in the sense that it does not lock execution between submitting the HTTP request to the remote server and processing the remote servers results. The copying of the remote server’s result to the client’s response stream is still synchronous in the sense that reading and writing operations block each other. This is something you may want to change if you have a very high number of requests on your server. For most practical purposes on personal home servers this should never be an issue.
The proxy has support for gzip and deflate compression between the remote server and IIS. However, the response returned to the client is currently uncompressed, regardless of the client’s accepted content encodings.
The Log class implements the logging functionality for the Windows Event Log. Since only static methods of the EventLog class are used, the logging functions are thread safe.
Known Bugs and Issues
There are no known bugs.
- Content compression between proxy and web browser is currently not supported.
- Character sets are currently not preserved in the content type header. This may cause issues with Unicode websites.
- HTTP Authorization is currently not supported.
Related Resources
For the implementation of the reverse proxy I borrowed ideas from the following projects: