Shawn Cicoria - CedarLogic

Perspectives and Observations on Technology

Recent Posts

Sponsors

Tags

General





Community

Email Notifications

Blogs I Read

Archives

Other

Use OpenDNS

March 2011 - Posts

Differences when Running with OutputCache managed module under ASP.NET IIS7.x with Cache-control header

This post is to report some differences when using MVC or IHttpHandlers if you’re attempting to set the Cache-control : max-age or s-maxage value under IIS7.x using the HttpResponse.Cache methods.

[UPDATE]: 2011-3-14 – The missing piece was calling  Response.Cache.SetSlidingExpiration(true) as follows:

context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetMaxAge(TimeSpan.FromMinutes(5));
context.Response.ContentType = "image/jpeg";
context.Response.Cache.SetSlidingExpiration(true);

 

Under IIS7.x if you us one of the following 2 methods, you will only get a Cache-ability of “public”. 

public ActionResult Image2()
{
    MemoryStream oStream = new MemoryStream();

    using (Bitmap obmp = ImageUtil.RenderImage("Respone.Cache.Setxx calls", 5, DateTime.Now))
    {
        obmp.Save(oStream, ImageFormat.Jpeg);
        oStream.Position = 0;
        Response.Cache.SetCacheability(HttpCacheability.Public);
        Response.Cache.SetMaxAge(TimeSpan.FromMinutes(5));
        return new FileStreamResult(oStream, "image/jpeg");
    }
}

Method 2 – which is just a plain old HttpHandler and really isn’t MVC3, but under the same MVC ASP.NET application, same result.

public class image : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        using (var image = ImageUtil.RenderImage("called from IHttpHandler direct", 5, DateTime.Now))
        {
            context.Response.Cache.SetCacheability(HttpCacheability.Public);
            context.Response.Cache.SetMaxAge(TimeSpan.FromMinutes(5));
            context.Response.ContentType = "image/jpeg";
            image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
        }            
    }


}

Using the following under MVC3 (I haven’t tried under earlier versions) will work by applying the OutputCacheAttribute to your Action:

[OutputCache(Location = OutputCacheLocation.Any, Duration = 300)]
public ActionResult Image1()
{
    MemoryStream oStream = new MemoryStream();

    using (Bitmap obmp = ImageUtil.RenderImage("called with OutputCacheAttribute", 5, DateTime.Now))
    {
        obmp.Save(oStream, ImageFormat.Jpeg);
        oStream.Position = 0;
        return new FileStreamResult(oStream, "image/jpeg");
    }
}

To remove the “OutputCache” module, you use the following in your web.config:
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true">
  <!--<remove name="OutputCache"/>-->
</modules>
Taking advantage of Windows Azure CDN and Dynamic Pages in ASP.NET - Caching content from hosted services

With the updates to Windows Azure CDN announced this week [1] I wanted to help illustrate the capability with a working sample that will serve up dynamic content from an ASP.NET site hosted in a WebRole.

First, to get a good overview of the capability you can read the Overview of the Windows Azure CDN [2] content on MSDN.

When you setup the ability to cache content from a hosted service, the requirement is to provide a path to your role’s DNS endpoint that ends in the path “/cdn”.  Additionally, you then map CDN to that service.

SNAGHTML520b6ac

What WAZ CDN does, is allow you to then map that through the CDN to your host.  The CDN will then make a request to your host on your client’s behalf.

The requirement is still that your client, and any Url’s that are to be serviced through the CDN and this capability have to use the CDN DNS name and not your host – no different than what CDN does for Blob storage.

The following 2 URL’s are samples of how the client needs to issue the requests.

Windows Azure hosted service URL: http: //myHostedService.cloudapp.net/cdn/music.aspx   - for regular “dynamic” content

Windows Azure CDN URL: http: //<identifier>.vo.msecnd.net/music.aspx   - for CDN “cachable” content.

The first URL path’s the request direct to your host into the Azure datacenter.  The 2nd URL paths the request through the CDN infrastructure, where CDN will make the determination to request the content on behalf of the client to the Azure datacenter and your host on the /cdn path.

The big advantage here is you can apply logic to your content creation.  What’s important is emitting the CDN friendly headers that allow CDN to request and re-request only when you designate based upon it’s rules of “staleness” as described in the overview page.

With IIS7.5 there is an underlying issue when the Managed Module “OutputCache” is enabled that in order to emit a good header for your content, you’ll need to remove, and in my sample, helps provide CDN friendly headers.  You get IIS 7.5 when running under OS Family “2” in your service configuration.

NOTE: Re: IIS7.x and OutputCache handling of Cache-control header

[UPDATE]: 2011-3-14 – The missing piece was calling  Response.Cache.SetSlidingExpiration(true) as follows:

Response.Cache.SetCacheability(HttpCacheability.Public);  
Response.Cache.SetMaxAge(TimeSpan.FromMinutes(5));  
Response.ContentType = "image/jpeg";  
Response.Cache.SetSlidingExpiration(true); 

By default, and when the OutputCache managed module is loaded, if you use the HttpResponse.CachePolicy to set the Http Headers for “max-age” when the HttpCacheability is “Public”, you will NOT get the “max-age” emitted as part of the “Cache-control:” header.  Instead, the OutputCache module will remove “max-age” and just emit “public”.  It works ok when Cacheability is set to “private”.

To work around the issue and ensure your code as follows emits the full max-age along with the public option, you need to remove as follows:

<system.webServer> 
  <modules runAllManagedModulesForAllRequests="true"> 
    <remove name="OutputCache"/> 
  </modules> 
</system.webServer> 
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetMaxAge(TimeSpan.FromMinutes(rv));

UPDATE: 2011-3-13 – CDN will also respect the s-maxage Cache-control value part of the header. This can be set with the HttpResponse.Cache.SetProxyMaxAge method – similar to SetMaxAge.

In the attached solution, the way I approached it was to have a VirtualApplication under the root site that has it’s own web.config  - this VirtualApplication is the /cdn of the site and when deployed to Azure as a Web Role will surface as a distinct IIS Application – along with a separate AppDomain.

The CDN Sample is a simple Web Forms site that the /default landing page contains 3 IFrames to host:

1. Content direct from the host @   http://xxxx.cloudapp.net/cdn

2. Content via the CDN @ http://azxxx.vo.msecnd.net 

3. Simple list of recent requests – showing where the request came from.

CDNSampleTest

 

When you run the sample the first time you hit the page, both the Host and the CDN will cause 2 initial requests to hit the host.  You won’t see the first requests in the list because of timing – but if you refresh, you’ll see that the list will show that you have 2 requests initially.

1. sourced direct from the Browser to the HOST

2. sourced via the CDN

The picture above shows the call-outs of each of those requests – green rows showing requests coming direct to the HOST, yellow showing the CDN request.  The IP addresses of the green items are direct from the client, where the CDN is from the CDN data center.

As you refresh the page (hit Ctrl+F5 to force a full refresh and avoid “304 – not changed”) you’ll see that the request to the HOST get’s processed direct; but the request to the CDN endpoint is serviced direct from the CDN and doesn’t incur any additional request back to the HOST.

The following is the Headers from the CDN response

(Status-Line)    HTTP/1.1 200 OK
Age    13
Cache-Control    public, max-age=300
Connection    keep-alive
Content-Length    6212
Content-Type    image/jpeg; charset=utf-8
Date    Fri, 11 Mar 2011 20:47:14 GMT
Expires    Fri, 11 Mar 2011 20:52:01 GMT
Last-Modified    Fri, 11 Mar 2011 20:47:02 GMT
Server    Microsoft-IIS/7.5
X-AspNet-Version    4.0.30319
X-Powered-By    ASP.NET

 

The following are the Headers from the HOST response

(Status-Line)    HTTP/1.1 200 OK
Cache-Control    public, max-age=300
Content-Length    6189
Content-Type    image/jpeg; charset=utf-8
Date    Fri, 11 Mar 2011 20:47:15 GMT
Last-Modified    Fri, 11 Mar 2011 20:47:02 GMT
Server    Microsoft-IIS/7.5
X-AspNet-Version    4.0.30319
X-Powered-By    ASP.NET

 

You can see that with the CDN request, the countdown (age) starts for aging the content.

The full sample is located here:

Update: 2011-3-13: Project has been updated a bit; more Compute Emulator friendly – 1 thing to note, since it’s using a VirtualApplication to debug the /cdn part, you have to attach manually to the w3wp.exe on your development machine.

CDNSampleSite.zip

[1] http://blogs.msdn.com/b/windowsazure/archive/2011/03/09/now-available-updated-windows-azure-sdk-and-windows-azure-management-portal.aspx

[2] http://msdn.microsoft.com/en-us/library/ff919703.aspx

Asus Slate–I gotta get one.

This thing rocks…

AD FS 2.0: Troubleshooting Event 364 and ThrowExceptionForHRInternal / NullReferenceException

 

Ran into a situation today where after AD FS federation server was installed, configured and up & running, “all of a sudden” it stopped working.

Turned out that another installer that affected the default web site, also seemingly affected the AppPools associated to all Applications under the Default Web site.

By changing the “Enable 32-bit Applications” either through IIS admin or via command line

appcmd set apppool /apppool.name:MyAppPool /enable32BitAppOnWin64:false

Back to normal…

 

SNAGHTML32c976

Posted: 03-04-2011 12:17 PM by cicorias | with no comments
Filed under: ,
Running Jetty under Windows Azure Using RoleEntryPoint in a Worker Role

This post is built upon the work of Mario Kosmiskas and David C. Chou’s prior postings – from here:

http://blogs.msdn.com/b/mariok/archive/2011/01/05/deploying-java-applications-in-azure.aspx 

http://blogs.msdn.com/b/dachou/archive/2010/03/21/run-java-with-jetty-in-windows-azure.aspx

As Mario points out in his post, when you need to have more control over the process that starts, it generally is better left to a RoleEntryPoint capability that as of now, requires the use of a CLR based assembly that is deployed as part of the package to Azure.

There were things I liked especially about Mario’s post – specifically, the ability to pull down the JRE and Jetty runtimes at role startup and instantiate the process using the extracted bits.  The way Mario initialized the java process (and Jetty) was to take advantage of a role startup task configured as part of the service definition.  This is a great quick way to kick off processes or tasks prior to your role entry point.  However, if you need access to service configuration values or role events, that’s where RoleEntryPoint comes in.  For this PoC sample I moved the logic for retrieving the bits for the jre and jetty to the worker roles OnStart – in addition to moving the process kickoff to the OnStart method.  The Run method at this point is there to loop and just report the status of the java process. Beyond just making things more parameterized, both Mario’s and David’s articles still form the essence of the approach.

The solution that accompanies this post provides all the necessary .NET based Visual Studio project.  In addition, you’ll need:

1. Jetty 7 runtime http://www.eclipse.org/jetty/downloads.php

2. JRE http://www.oracle.com/technetwork/java/javase/downloads/index.html

Once you have these the first step is to create archives (zips) of the distributions.  For this PoC, the structure of the archive requires that the root of the archive looks as follows:

JRE6.zip

3-3-2011 6-55-32 PM

jetty---.zip

3-3-2011 6-56-37 PM

Upload the contents to a storage container (block blob), and for this example I used /archives as the location.  The service configuration has several settings that allow, which is the advantage of using RoleEntryPoint, the ability to provide these things via native configuration support from Azure in a worker role.

Storage Explorer

3-3-2011 7-00-17 PM

You can use development storage for testing this out – the zipped version of the solution is configured for development storage.  When you’re ready to deploy, you update the two settings – 1 for diagnostics and the other for the storage container where the /archives are going to be stored.

<?xml version="1.0" encoding="utf-8"?>
<ServiceConfiguration serviceName="HostedJetty"
       xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration"
       osFamily="2"
       osVersion="*">
  <Role name="JettyWorker">
    <Instances count="1" />
    <ConfigurationSettings>
      <!--<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"
value="DefaultEndpointsProtocol=https;AccountName=<accountName>;AccountKey=<accountKey>" />-->
      <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"
value="UseDevelopmentStorage=true" />
      <Setting name="JettyArchive"
value="jetty-distribution-7.3.0.v20110203b.zip" />
      <Setting name="StartRole"
value="true" />
      <Setting name="BlobContainer"
value="archives" />
      <Setting name="JreArchive"
value="jre6.zip" />
      <!--<Setting name="StorageCredentials"
value="DefaultEndpointsProtocol=https;AccountName=<accountName>;AccountKey=<accountKey>"/>-->
      <Setting name="StorageCredentials"
value="UseDevelopmentStorage=true" />

 

For interacting with Storage you can use several tools – one tool that I like is from the Windows Azure CAT team located here: http://appfabriccat.com/2011/02/exploring-windows-azure-storage-apis-by-building-a-storage-explorer-application/  and shown in the prior picture

At runtime, during role initialization and startup, Azure will call into your RoleEntryPoint.  At that time the code will do a dynamic pull of the 2 archives and extract – using the Sharp Zip Lib <link> as Mario had demonstrated in his sample.  The only different here is the use of CLR code vs. PowerShell (which is really CLR, but that’s another discussion).

At this point, once the 2 zips are extracted, the Role’s file system looks as follows:

Worker Role approot

3-3-2011 6-17-12 PM

From there, the OnStart method (which also does the download and unzip using a simple StorageHelper class) kicks off the Java path and now you have Java!

Task Manager

3-3-2011 6-16-10 PM

Jetty Sample Page

3-3-2011 6-58-49 PM

A couple of things I’m working on to enhance this is to extract the jre and jetty bits not to the appRoot but to a resource location defined as part of the service definition.

ServiceDefinition.csdef

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="HostedJetty" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WorkerRole name="JettyWorker">
<Imports>
  <Import moduleName="Diagnostics" />
  <Import moduleName="RemoteAccess" />
  <Import moduleName="RemoteForwarder" />
</Imports>
<Endpoints>
  <InputEndpoint name="JettyPort" protocol="tcp" port="80" localPort="8080" />
</Endpoints>
<LocalResources>
  <LocalStorage name="Archives" cleanOnRoleRecycle="false" sizeInMB="100" />
</LocalResources>

 

As the concept matures a bit, being able to update dynamically the content or jar files as part of a running java solution is something that is possible through continued enhancement of this simple model.

The Visual Studio 2010 Solution is located here:

HostingJavaSln_NDA.zip

Posted: 03-03-2011 4:34 PM by cicorias | with no comments
Filed under: , ,
Azure VS Tools and SDK - systray already running…

If you are getting a message when you start the Compute Emulator “Systray already running…” from within Visual Studio one fix is to check what the image name is loading is.

For some reason, on 2 of my machines the image was loading with the 8.3 format.  This caused the logic in the VS tools to not find the process. 

So, to fix, I just did a little copy/rename magic.

C:\Program Files\Windows Azure SDK\v1.3\bin>copy csmonitor.exe csmonitor-a.exe
        1 file(s) copied.
C:\Program Files\Windows Azure SDK\v1.3\bin>del csmonitor.exe

C:\Program Files\Windows Azure SDK\v1.3\bin>copy csmonitor-a.exe csmonitor.exe
        1 file(s) copied.

If you bring up task manager and see something like CSMON~1.EXE in the Image Name column, you probably have this issue.

Posted: 03-02-2011 6:24 PM by cicorias | with no comments
Filed under: ,