Wednesday, May 30, 2007

Do you want to save about 500kb traffic for every anonymous request to your MOSS 2007 Publishing Site?

There is no default way in MOSS 2007 where you can say: "I'm online visible for anonymous users, I don't want to load core.js, portal.js, init.js, ... every time a user is accessing a page". I was searching for this switch a long time, but it doesn't seems to exist.

 

Microsoft's proposal for this is to force a delayed loading of the core.js, which can be read here: http://support.microsoft.com/kb/933823/en-us or here: http://blogs.msdn.com/ecm/archive/2007/02/21/building-a-new-page-layout-which-does-not-reference-core-js-but-downloads-it-while-the-page-is-being-viewed-thereby-optimizing-response-time.aspx. This doesn't solve the problem, but is one way to speed up your pages.

 

A solution for this problem is a HTTP-Filter. This filter can be deployed in the /bin directory of your web application and has to be referenced in your web.config.

 

The solution below is not perfect, but it works and saves a lot of traffic, server load, time and money. And it enables you to publish lightweight pages with MOSS 2007.

 

Improvements for the code below would be a separation of the "tags to cleanup" in a configuration file and the usage of regular expressions. But with output cache enabled, this shouldn't be a performance issue.

 

Make sure to adapt this code for your own needs, as I'm removing e.g. ViewState and so on, which could possibly crash your MOSS applications.

MOSSCleanupModule.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using Microsoft.SharePoint;
namespace HttpModules
{
   
public class MOSSCleanupModule : IHttpModule
   
{
      
public void Init(HttpApplication app)
      {
         app.ReleaseRequestState +=
new EventHandler(InstallResponseFilter);
      }

      private void InstallResponseFilter(object sender, EventArgs e)
      {
         HttpResponse response = HttpContext.Current.Response;
         HttpRequest request = HttpContext.Current.Request;
         if (response.ContentType == "text/html" && request.Url.AbsolutePath.EndsWith(".aspx") && !request.Url.AbsolutePath.Contains
            ("_layouts/"))
         {
            SPUser user = SPContext.Current.Web.CurrentUser;
            if (user == null || string.IsNullOrEmpty(user.LoginName))
            {
               response.Filter =
new MOSSCleanupFilter(response.Filter);
            } 
         }
      }

      public void Dispose()
      {
      }
   }
}



MOSSCleanupFilter.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
namespace HttpModules
{
public class MOSSCleanupFilter : Stream
{
   private static string[] completeTagsToCleanup = new string[] 
   {
      "<meta name=\"GENERATOR\" content=\"Microsoft SharePoint\" />",
      "<input type=\"hidden\" name=\"__SPSCEditMenu\" id=\"__SPSCEditMenu\" value=\"true\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOWebPartPage_PostbackSource\" id=\"MSOWebPartPage_PostbackSource\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOTlPn_SelectedWpId\" id=\"MSOTlPn_SelectedWpId\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOTlPn_View\" id=\"MSOTlPn_View\" value=\"0\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOTlPn_ShowSettings\" id=\"MSOTlPn_ShowSettings\" value=\"False\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOGallery_SelectedLibrary\" id=\"MSOGallery_SelectedLibrary\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOGallery_FilterString\" id=\"MSOGallery_FilterString\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOTlPn_Button\" id=\"MSOTlPn_Button\" value=\"none\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOAuthoringConsole_FormContext\" id=\"MSOAuthoringConsole_FormContext\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOAC_EditDuringWorkflow\" id=\"MSOAC_EditDuringWorkflow\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOSPWebPartManager_DisplayModeName\" id=\"MSOSPWebPartManager_DisplayModeName\" value=\"Browse\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOWebPartPage_Shared\" id=\"MSOWebPartPage_Shared\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOLayout_LayoutChanges\" id=\"MSOLayout_LayoutChanges\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOLayout_InDesignMode\" id=\"MSOLayout_InDesignMode\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOSPWebPartManager_OldDisplayModeName\" id=\"MSOSPWebPartManager_OldDisplayModeName\" value=\"Browse\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOSPWebPartManager_StartWebPartEditingName\" id=\"MSOSPWebPartManager_StartWebPartEditingName\" value=\"false\" />"+System.Environment.NewLine,
      "_spBodyOnLoadWrapper();",
   };

   private static string[] tagsToCleanup = new string[] 
   {
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1033/styles/core.css?rev=",
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/Style%20Library/en-US/Core%20Styles/Band.css",
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/Style%20Library/en-US/Core%20Styles/Controls.css",
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1033/styles/HtmlEditorCustomStyles.css?rev=",
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1033/styles/HtmlEditorTableFormats.css?rev=",
      "<input type=\"hidden\" name=\"__REQUESTDIGEST\" id=\"__REQUESTDIGEST\"",
      "<input type=\"hidden\" name=\"__VIEWSTATE\" id=\"__VIEWSTATE\" value=",
   };

   private static string[] scriptsToCleanup = new string[] 
   {
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/core.js?rev=",
      "<script src=\"/WebResource.axd?",
      "<script> var MSOWebPartPageFormName = 'aspnetForm'",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/init.js?rev=",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/init.js?rev=",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/portal.js?rev=",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/ie55up.js?rev=",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/non_ie.js?rev=",
      "<script type=\"text/javascript\">"+System.Environment.NewLine+"<!--"+System.Environment.NewLine+"var __wpmExportWarning",
      "<script type=\"text/JavaScript\" language=\"JavaScript\">"+System.Environment.NewLine+"<!--"+System.Environment.NewLine+"var L_Menu_BaseUrl",
   };

   private Stream responseStream;
   
private long position;

   public MOSSCleanupFilter(Stream inputStream)
   {
      this.responseStream = inputStream;
   }

   public override void Write(byte[] buffer, int offset, int count)
   {
      StringBuilder html = new StringBuilder(System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count));
      foreach(string completeTagToClean in completeTagsToCleanup) 
      {
         this.CleanUp(html, completeTagToClean);
      }

      foreach (string tagToClean in tagsToCleanup)
      {
         this.CleanUp(html, tagToClean, ">");
      }

      foreach (string scriptToClean in scriptsToCleanup)
      {
         this.CleanUp(html, scriptToClean, "</script>");
      }

      byte
[] data = System.Text.UTF8Encoding.UTF8.GetBytes(html.ToString());
      responseStream.Write(data, 0, data.Length);
   }

   private void CleanUp(StringBuilder html, string search, string endTag)
   {
      int startPos = html.ToString().IndexOf(search);
      if (startPos != -1)
      {
         if (!string.IsNullOrEmpty(endTag))
         {
            int endPos = html.ToString().IndexOf(endTag, startPos);
            if (endPos != -1)
            {
               html.Remove(startPos, endPos - startPos + endTag.Length);
            }
         }
         else
         {
            html.Remove(startPos, search.Length);
         }
      }
   }

   private
void CleanUp(StringBuilder html, string search)
   {
      
this.CleanUp(html, search, null);
   }

   #region Filter overrides
   
   
public override bool CanRead
   {
      get
      {
         return true;
      }
   }

   public
override bool CanSeek
   {
      get
      {
         return true;
      }
    }

   public override bool CanWrite
   {
      get
      {
         return true;
      }
   }

   public override void Close()
   {
      responseStream.Close();
   }

   public override void Flush()
   {
      responseStream.Flush();
   }

   public override long Length
   {
      get
      {
         return 0;
      }
   }

   public override long Position
   {
      get
      {
         return position;
      }
      set
      {
         position =
value;
      }
   }

   public override long Seek(long offset, SeekOrigin origin)
   {
      return responseStream.Seek(offset, origin);
   }

   public override void SetLength(long length)
   {
      responseStream.SetLength(length);
   }

   public override int Read(byte[] buffer, int offset, int count)
   {
      return responseStream.Read(buffer, offset, count);
   }
   #endregion
   }
}



web.config:

<httpModules>
...

   <
add name="MOSSCleanupModule" type="HttpModules.MOSSCleanupModule, HttpModules, Version=1.0.0.1, Culture=neutral, PublicKeyToken=..."/>
...
</httpModules>