Showing posts with label IIS. Show all posts
Showing posts with label IIS. Show all posts

Thursday, February 9, 2012

Cache implementation for your Web & WCF resource


There are different ways of implementing the caching.
1.       At the server, where you store data in-memory using the ASP.Net Cache / Application classes.
2.       In the client (Browser). This definitely gives more advantage in terms of performance.

This article describes in detail about the second type of caching.


What are the resources that can be cached?
Static resources like html, scripts - js, style sheets – css, images – gif, jpg etc
Ex: http://www.domain.com/images/banner.jpg
Dynamic resources which are REST based urls  - if you think that these resources don’t change their data very frequently then they can be a good candidate for caching.
Ex:

                                                               
What do we intend to save by caching on the browser?
Loading the server to fetch the data and pass it across
Even making an http request to the server. There are other ways to save the number of requests to server by using minification process. Where we can merge several js/css files into single file. You can read my previous articles to know more about it.


Implementing Caching
This is simple by adding Expires Header (when you know exactly when to expire) to the IIS. http://technet.microsoft.com/en-us/library/cc770661(WS.10).aspx
You can also use the Max Age header when you are not sure of the number days you need to cache the resource. The above link describes for both.


Challenges
Finding the exact Expires/MaxAge becomes hard as we will not be able to predict our bug fix cycles due to hot fixes etc.. specially in case of smaller projects.


Solution
Two common solutions both deal with manipulating the URL:
1.       Manipulate the calls to the resource by adding a query string. This requires to do a find a replace to all the locations where the resource is being referenced. Ex: http://www.domain.com/app/images/banner.jpg?rev=2345 . This becomes a painful process to manipulate it every time a resource is changed. There are very high chances that we may miss out modifying a revision query string. This also has some minor drawbacks that some proxy servers don’t cache urls which contain query string.

For REST based WCF Service, add this piece of code before the response.
OutgoingWebResponseContext ctx = WebOperationContext.Current.OutgoingResponse;
ctx.Headers.Add(HttpResponseHeader.CacheControl, String.Format("public, max-age={0}", maxAge)); //in seconds.

2.       The second more robust solution “Fingerprinting” is to inject some kind of hash into the url. Ex: http://www.domain.com/CBUSTZqTMzNV8qFU/0.jpg
Here the part of the url before the file name is the fingerprint or a hash generated based on the contents of the resource being accessed. This requires that we use some mechanism like a URL Rewrite module to do a Rewrite of requests. Ex: Search for a pattern (.*)/CBUST[A-F0-9]{32}(/.*) and do a rewrite to {R:1}{R:2} For more information on URL Rewrite http://www.iis.net/download/urlrewrite
In your aspx files, you will reference your resources like <script type="text/javascript" src="<%=Utilities.CacheBusterUrl("/_layouts/scripts/jquery-1.4.3.min.js"%>">script>

Add this piece of code in your Page_Load if it is a page.
if (!this.Page.IsPostBack)
{
this.DataBind();
}

If it is a user/web control then ensure that the page that is adding this user/web control has the above piece of code.

The Fingerprinting method has its own limitation that any resource referenced in .css file or being rendered from SharePoint library cannot be cached like this.

Implementation of the CacheBuster utility is as follows.
    public static class Utilities
    {
        private static Hashtable Md5Map = Hashtable.Synchronized(new Hashtable());

        public static string CacheBusterUrl(string sourceUrl)
        {
            try
            {
                string filesystemPath = HttpContext.Current.Server.MapPath(sourceUrl);
                string fileMD5;
                lock (Md5Map.SyncRoot)
                {
                    if (Md5Map.ContainsKey(filesystemPath))
                    {
                        fileMD5 = Md5Map[filesystemPath].ToString();
                    }
                    else
                    {
                        fileMD5 = CreateHash(filesystemPath);
                        Md5Map.Add(filesystemPath, fileMD5);
                    }
                }
                return BuildCacheBusterURL(sourceUrl, fileMD5);
            }
            catch (Exception ex)
            {
                return sourceUrl;
            }
        }
    }


Security Warning
Be aware that if your Web Application stores cookies then caching resources at the proxy servers can be a security threat. So make sure to use https whenever you decide to store cookies.

Thursday, May 5, 2011

Can't find my SharePoint Web page on IIS?

Coming from a background of ASP.net, we are used to going to the IIS Manager and locating a web page file. In SharePoint, try finding it. You will be surprised. We usually map the URL relative to the Application directory of the WFE.
When you create a new Web Application in SharePoint it creates an entry in the IIS with the application by default being hosted on the Physical Directory C:\inetpub\wwwroot\wss\VirtualDirectories\ 
All Standard Pages/Pages that you created in the SharePoint Console, ideally should have been placed in this directory, but, instead SharePoint stores them in the Content database.


Then you must be wondering how SharePoint manages to find the correct page through the URL. 
ASP.Net came up with a new concept of VirtualPathProvider. To allow users to implement custom storage of the files (web pages) without affecting the way users access web pages. SharePoint has provided a custom implementation through SPVirtualPathProvider that connects the URL with the appropriate page from the Content Database.
The SPVirtualPathProvider class works together with another class named the SPPageParserFilter to supply processing instructions to the ASP.NET page parser. For example, the SPPageParserFilter component controls whether the ASP.NET page parser compiles the ASP.NET page into an assembly DLL or whether it processes the page in a no-compile mode that is introduced with ASP.NET 2.0




Not all standard pages come from SharePoint Content database. Some pages are hosted within the SharePoint root folder. C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\
SPVirtualPathProvider will know from where a particular page has to be picked up from (File System vs. Database). These kinds of pages are called as Ghosted Pages. The pages that come from Content database are called UnGhosted pages. These are customized pages of the standard pages.


SharePoint version of the life cycle is depicted in the below image. They integrate with the ASP.Net runtime seamlessly by adding the SPHttpApplication (HttpApplication replacement for the ASP.net) containing a Module SPRequestModule with a couple of common ASP.Net modules. This finally uses the SPHttpHandler (SharePoint implementation of HttpHandler) to process requests.
Global.asax would contain a directive as 
<@Application Inherits="Microsoft.SharePoint.ApplicationRuntime.SPHttpApplication" >
Http Module and Http Handers will be registered with the application through the Web.Config.

A quick view of the IIS brings us to a couple of Virtual Directories of a SharePoint web application. If you follow the Physical path of these they point to the Root folder.
Note: The image is from the MOSS. However, they are pretty much the same with a few additions in SharePoint 2010.
  • _vit_bin : Provides Windows SharePoint Services with a way to expose DLLs and .asmx Web service files at a path within the URL space of a Web application. 
  • _controltemplates : Provides a repository for deploying ASP.NET user controls that can be used within pages. 
  • _wpresources : Provides a repository for resource files that are deployed along with Web Parts.
  • layouts :  Provides the repository for application pages. 
There is only one version of an application page scoped at the farm level, it can be compiled into a single DLL and loaded into memory once for each Web application. 
An application page, such as settings.aspx, can be accessed by adding its relative path within the _layouts directory to the end of a site’s URL
http://Litwareinc.com/_layouts/settings.aspx 
http://Litwareinc.com/sites/Vendors/_layouts/settings.aspx  
http://Litwareinc.com:1001/sites/Accounting/_layouts/settings.aspx