2

I'm on SSRS 2012 in Native mode. The configuration doesn't seem to have changed much from SSRS 2008, which I upgraded from, so the issue applies to both versions (however, if there's a solution, it may or may not apply to both).

I am in a situation where I would like to serve up the ReportManager web site from one set of ports on the external side of a firewall, but internally, have SSRS setup on a different port. Does anyone know how this can be accomplished?

Details

The reason this is an issue is because the SSRS code insists on constructing full, absolute URLs to the different locations within the ReportManager web site. (IMO, this is just one of many significant/crippling design flaws in SSRS) If it would just use URLs that didn't attempt to specify the domain+port, this wouldn't be an issue.

Example

Let's start with an external URL of "http://reports.example.com/ssrs", and ReportManager is configured to live on port 8080 (your port forward goes from port 80 to 8088, of course). If you go to the external URL, you'll hit the site just fine; let's ignore the AuthC subsystem and assume you logged in OK. For some of the links (e.g. "Details View", "Move", "Delete", etc.), now, you won't go where you expected because SSRS tries to send you to "http://reports.example.com:8080/ssrs/...". You can manually fix the link by removing the port, and you'll get to the page. There are differences in what URLs are broken between SSRS 2012 and SSRS 2008, but the issue is still there.

Granger
  • 1,150
  • 2
  • 10
  • 26

1 Answers1

1

I have something that works on SSRS 2012, but it's a total hack. It's possible to use client-side Javascript to rewrite all the URLs (plus the occasional RedirectUrl parameter SSRS is fond of), but that doesn't account for the Response.Redirect() calls server-side. So you'd be left with a partial solution.

In short, add an HttpModule to ReportManager so you can add a PreRequestHandlerExecute event handler. Inside it, use Reflection to fix GlobalApp.BaseUrl and Request.Url to go off the HOST header instead of the default. You have to change both because SSRS doesn't always use its own BaseUrl when building links. (I used ILSpy to find what fields to change.)

Here's mine. I'm sure it could be done better, but what I have works and I don't really care anymore. Working on SSRS has a way of sucking the will to live out of you.

EDIT: Fixed the part that modifies Request.Url. Note that while it's specifying .Fragment, the client doesn't send that, so it's always blank.

void context_PreRequestHandlerExecute(object sender, EventArgs ea)
{
    HttpApplication app = sender as HttpApplication; // also at: HttpContext.Current.ApplicationInstance
    string host = app.Context.Request.Headers["HOST"];
    if (!string.IsNullOrEmpty(host))
    {
        System.Reflection.FieldInfo fi = typeof(Microsoft.ReportingServices.UI.GlobalApp).GetField("m_baseUrl", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        if (fi != null)
        {
            string protocol = "http://";
            if (app.Context.Request.IsSecureConnection)
                protocol = "https://";
            Uri url = new Uri(string.Format("{0}{1}{2}", protocol, host, app.Context.Request.ApplicationPath));
            fi.SetValue(app as Microsoft.ReportingServices.UI.GlobalApp, url);

            fi = typeof(System.Web.HttpRequest).GetField("_url", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            if (fi != null)
            {
                url = new Uri(string.Format("{0}{1}{2}{3}{4}", protocol, host, app.Context.Request.FilePath, app.Context.Request.Url.Query, app.Context.Request.Url.Fragment));
                fi.SetValue(app.Context.Request, url);
            }
        }
    }
}
Granger
  • 1,150
  • 2
  • 10
  • 26