Western Mass Hosting's Source Code Articles

Home / Source Code / Page 5

Cookie Notice

This site utilizes cookies to improve your browsing experience, analyze the type of traffic we receive, and serve up proper content for you. If you wish to continue browsing, you must agree to allow us to set these cookies. If not, please visit another website.

Speed Up The Web - Part II

Welcome my friends to a second installment and follow-up to our Speed Up The Web series.

In this installment we will be discussing another similar technique that was previously discussed in Part I.

Minifying and Concatenating your stylesheets and javascripts.
The previous process discussed a method to do this for your .Net website automatically by parsing the outputted HTML for your stylesheets and scripts, combining them into one string each, saving the strings to server cache and finally rendering them through a gzipped output.

This is all done by utilizing a .Net HttpModule.

I thought it would be the perfect solution, but have since found out otherwise. While the methology involved remains the same, the delivery method is what is changing. I have found that if you utilize Master Pages (or have developed a highly customizable CMS system like myself), this method simply does not work.

Thus, I set myself the task of making something work.

What I found was that by using a Response.Filter on the IO.Stream of the HTML rendered, we can accomplish the same goals, without any modifications to your web.config file, and without the use of an HttpModule.

In my test configuration, I simply have 3 files (default.aspx, default2.aspx, and default3.aspx);

  • Default.aspx simply is an .Net HTML file with 2 references to 2 seperate stylesheet files, with the filter applied on Page_Load in the code behind
  • Default2.aspx simply contains a placeholder reference for the Master Page we use for it, which happens to contain the same structure as Default.aspx
  • Default3.aspx is the same file as Default.aspx, except it does not contain the filter in the code behind.

For testing we used FireFox, and the handy dandy FireBug plugin, and the results were as such:

  • Default.aspx – Fresh Load – Non-Cached: 3 Requests = 2.25s | Cached: 3 Requests From Cache = 1.67s
  • Default2.aspx – Fresh Load – Non-Cached: 3 Requests = 2.15s | Cached: 3 Requests From Cache = 1.77s
  • Default3.aspx – Fresh Load – Non-Cached: 7 Requests = 3.32s | Cached: 7 Requests No Cache = 3.25s

The biggest thing to note here is the number of requests.

Complete File Listing:

  • /App_Code/Common.vb
  • /App_Code/DynamicCompression.vb
  • /Scripts/custom.js
  • /Scripts/jquery_latest.js
  • /Scripts/jquery.pngFix.pack.js.js
  • /Scripts/jquery.preloadcssimages.js.js
  • /Styles/layout1.css
  • /Styles/layout2.css
  • /Default.aspx w/Code Behind
  • /Default2.aspx w/Code Behind
  • /Default3.aspx w/Code Behind
  • /MasterPage.master w/Code Behind
  • /Script.aspx w/o Code Behind
  • /Style.aspx w/o Code Behind
  • /web.config

And now, without further ado… the code (I won’t bother posting the code for the scripts and styles….that’ll be up to you to do):

/App_Code/Common.vb

Imports Microsoft.VisualBasic

'''

''' Our common class file
'''

'''
Public Class Common

'''

''' Let's cache this stuff
'''

'''
Public Shared Sub CacheIt()
' Allow the browser to store resposes in the 'History' folder
HttpContext.Current.Response.Cache.SetAllowResponseInBrowserHistory(True)
' Set our cacheability to Public
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public)
' Resource is valid until the expiration has passed
HttpContext.Current.Response.Cache.SetValidUntilExpires(True)
' Set out last modified date to last year
HttpContext.Current.Response.Cache.SetLastModified(Date.Now.AddDays(-366))
' We want to store and cache the resource
HttpContext.Current.Response.AddHeader("Cache-Control", "store, cache")
' Set the Pragma to cache
HttpContext.Current.Response.AddHeader("pragma", "cache")
' Not sure if this one really works, but it doesn't throw an error, and Google likes resources served from a cookie-less domain... eh... worth a shot
HttpContext.Current.Response.AddHeader("Set-Cookie", "false")
' Make sure our cache control is Public
HttpContext.Current.Response.CacheControl = "public" '
'Set the expiration date of the resource until next year
HttpContext.Current.Response.Expires = 24 * 60 * 366
HttpContext.Current.Response.ExpiresAbsolute = DateAdd(DateInterval.Hour, 24 * 366, Date.Now)
End Sub

'''

''' Let's check to see if the browser accepts GZip encoding
'''

'''
'''
Public Shared Function IsGZipEnabled() As Boolean
Dim accEncoding As String = HttpContext.Current.Request.Headers("Accept-Encoding")
'Does the browser accept content encoding?
If (Not accEncoding Is Nothing) Then
If (accEncoding.Contains("gzip") Or accEncoding.Contains("deflate")) Then
Return True
Else
Return False
End If
Else
Return False
End If
End Function

End Class

/App_Code/DynamicComression.vb

Imports Microsoft.VisualBasic
Imports System.Web
Imports System.Text.RegularExpressions
Imports System.Text
Imports System.IO
Imports System.Web.Caching

Namespace ZipCM

'''

''' Our compressor class for miniying and concatenating javascript files and css files
'''

'''
Public Class Compressor
Inherits System.IO.Stream

#Region " Properties "

#Region " Required Properties by the IO.Stream Interface "

Public Overrides ReadOnly Property Length() As Long
Get

End Get
End Property

Public Overrides Property Position() As Long
Get

End Get
Set(ByVal value As Long)

End Set
End Property

Public Overrides ReadOnly Property CanRead() As Boolean
Get

End Get
End Property

Public Overrides ReadOnly Property CanSeek() As Boolean
Get

End Get
End Property

Public Overrides ReadOnly Property CanWrite() As Boolean
Get

End Get
End Property

#End Region

#Region " Internal Properties "

Private HTML As String, FileCSS As String, FileJS As String
Private context As HttpContext
Private PageID As Long
Private Base As System.IO.Stream

#End Region

#End Region

#Region " Methods "

#Region " Public "

#Region " Required by IO.Stream "

Public Overrides Sub Flush()

End Sub

Public Overrides Function Read(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) As Integer
Return Me.Base.Read(buffer, offset, count)
End Function

Public Overrides Function Seek(ByVal offset As Long, ByVal origin As System.IO.SeekOrigin) As Long

End Function

Public Overrides Sub SetLength(ByVal value As Long)

End Sub

#End Region

'''

''' Fire up our class passing in our Response Stream
'''

''''''
Public Sub New(ByVal ResponseStream As System.IO.Stream)
' Just in case it does not exist, let's throw up ;)
If ResponseStream Is Nothing Then Throw New ArgumentNullException("ResponseStream")
' Set our Response to the IO.Stream
Me.Base = ResponseStream
End Sub

'''

''' We want to overwrite our default Write method so we can do our stuff
'''

''''''''''''
Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)
' Get HTML code from the Stream
HTML = System.Text.Encoding.UTF8.GetString(buffer, offset, count)
'Compress and Concatenate the CSS
Me.CompressStyles(HTML)
'Compress and Concatenate the JS
Me.CompressJavascript(HTML)
' Send output, but replace some unneeded stuff first, like tabs, and multi-spaces
HTML = HTML.Replace(vbTab, String.Empty).Replace(" ", String.Empty).Replace(vbCrLf, String.Empty)
' Set the buffer to the Bytes gotten from our HTML
buffer = System.Text.Encoding.UTF8.GetBytes(HTML)
' Write It Out (not unlike 'Spit It Out - Slipknot'
Me.Base.Write(buffer, 0, buffer.Length)
End Sub

#End Region

#Region " Internal "

'''

''' Compress and Concatenate Our Style Sheets
'''

''''''
Private Sub CompressStyles(ByVal StrFile As String)
Dim FilesM As MatchCollection, FileName As String
' Grab all references to stylesheets as they are in our HTML
FilesM = Regex.Matches(StrFile, " <link.*?href=""(.*?)"".*?>")
Dim M(FilesM.Count - 1) As String
' Now that we have all our files in a match collection,
' we'll loop through each file and put each line together into one string
For i As Long = 0 To M.Length - 1
M(i) = FilesM(i).Groups(1).Value
FileName = HttpContext.Current.Server.MapPath(M(i).Replace("/", ""))
FileName = FileName.Replace("\", "")
' Make sure the file exists locally, then read the entire thing to add into our string
If File.Exists(FileName) Then
Using objFile As New StreamReader(FileName)
FileCSS += objFile.ReadToEnd
objFile.Close()
End Using
' Now that we have our concatenated string,
' let's replace the unneeded stuff
' Comments
FileCSS = Regex.Replace(FileCSS, "/*.+?*/", "", RegexOptions.Singleline)
' 2 Spaces
FileCSS = FileCSS.Replace(" ", String.Empty)
' Carriage Return
FileCSS = FileCSS.Replace(vbCr, String.Empty)
' Line Feed
FileCSS = FileCSS.Replace(vbLf, String.Empty)
' Hard Return
FileCSS = FileCSS.Replace(vbCrLf, String.Empty)
' Tabs w/ a single space
FileCSS = FileCSS.Replace(vbTab, " ")
FileCSS = FileCSS.Replace("t", String.Empty)
' Get rid of spaces next to the special characters
FileCSS = FileCSS.Replace(" {", "{")
FileCSS = FileCSS.Replace("{ ", "{")
FileCSS = FileCSS.Replace(" }", "}")
FileCSS = FileCSS.Replace("} ", "}")
FileCSS = FileCSS.Replace(" :", ":")
FileCSS = FileCSS.Replace(": ", ":")
FileCSS = FileCSS.Replace(", ", ",")
FileCSS = FileCSS.Replace("; ", ";")
FileCSS = FileCSS.Replace(";}", "}")
' Another comment
FileCSS = Regex.Replace(FileCSS, "/*[^*]**+([^/*]**+)*/", "$1")
End If
Next
Dim tmpCt As Long = 0
' One more loop to get rid of the multiple references,
' and replace it with a sigle reference to our new and improved stylesheet file
For Each tmpS As Match In FilesM
tmpCt += 1
If tmpCt = FilesM.Count Then
' If our count = the number of matches then replace
StrFile = StrFile.Replace(tmpS.Groups(0).ToString(), " ")
Else
' if not, just get rid of it
StrFile = StrFile.Replace(tmpS.Groups(0).ToString(), String.Empty)
End If
Next
FilesM = Nothing
' Need to put the New and Improved CSS string somewhere!
' What a better place for it, then the server cache
HttpContext.Current.Cache("CSS") = FileCSS
' We also need to return the improved HTML
HTML = StrFile
End Sub

'''

''' Compress Our Scripts
'''

''''''
Private Sub CompressJavascript(ByVal StrFile As String)
Dim FilesM1 As MatchCollection, FileName As String
' Grab all references to script files as they are in our HTML
FilesM1 = Regex.Matches(StrFile, "<script.*?src=""(.*?)"".*?>")
Dim M1(FilesM1.Count - 1) As String
' Now that we have all our files in a match collection,
' we'll loop through each file and put each line together into one string
For j As Long = 0 To M1.Length - 1
M1(j) = FilesM1(j).Groups(1).Value
FileName = HttpContext.Current.Server.MapPath(M1(j).Replace("/", ""))
FileName = FileName.Replace("\", "")
' Make sure the file exists locally, then read the entire thing to add into our string
If File.Exists(FileName) Then
Using objFile1 As New StreamReader(FileName)
FileJS += objFile1.ReadToEnd
objFile1.Close()
End Using
' Now that we have our concatenated string,
' let's replace the unneeded stuff
' Comments
FileJS = Regex.Replace(FileJS, "(// .*?$)", "", RegexOptions.Multiline)
FileJS = Regex.Replace(FileJS, "(/*.*?*/)", "", RegexOptions.Multiline)
' 2 Spaces with 1 Space
FileJS = FileJS.Replace(" ", " ")
' Carriage Return
FileJS = FileJS.Replace(vbCr, vbLf)
' Hard Return w/ Line Feed
FileJS = FileJS.Replace(vbCrLf, vbLf)
' Tabs with a single space
FileJS = FileJS.Replace(vbTab, " ")
End If
Next
Dim tmpCt1 As Long = 0
' One more loop to get rid of the multiple references,
' and replace it with a sigle reference to our new and improved stylesheet file
For Each tmpS As Match In FilesM1
tmpCt1 += 1
If tmpCt1 = FilesM1.Count Then
' If our count = the number of matches then replace
StrFile = StrFile.Replace(tmpS.Groups(0).ToString(), "") Else ' if not, just get rid of it StrFile = StrFile.Replace(tmpS.Groups(0).ToString(), String.Empty) End If Next FilesM1 = Nothing ' Need to put the New and Improved JS Somewhere! ' What a better place for it then the server cache HttpContext.Current.Cache("JS") = FileJS ' Also need to return the New and Improved HTML HTML = StrFile End Sub #End Region #End Region End Class End Namespace

/Default.aspx

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" Debug="true" %>

Ā 

Ā 

Just a test to make sure the CSS and JS works
Click Here

/Default.aspx.vb

Partial Class _Default
Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'Enable GZip Encoding for this page
If Common.IsGZipEnabled() Then
Dim accEncoding As String
accEncoding = Context.Request.Headers("Accept-Encoding")
If accEncoding.Contains("gzip") Then
Response.Filter = New System.IO.Compression.GZipStream(Response.Filter, System.IO.Compression.CompressionMode.Compress)
Response.AppendHeader("Content-Encoding", "gzip")
Else
Response.Filter = New System.IO.Compression.DeflateStream(Response.Filter, System.IO.Compression.CompressionMode.Compress)
Response.AppendHeader("Content-Encoding", "deflate")
End If
End If
'Cache our response in the browser
Common.CacheIt()
'Concatenate and Minify our Scripts and Stylesheets
Response.Filter = New ZipCM.Compressor(Response.Filter)
End Sub

End Class

/Default2.aspx – blank Code-Behind

<%@ Page Language="VB" MasterPageFile="~/MasterPage.master" AutoEventWireup="false" CodeFile="Default2.aspx.vb" Inherits="Default2" title="Untitled Page" %>

Just a test to make sure the CSS and JS works
Click Here

/Default3.aspx – Blank Code-Behind, contains the exact same code as /Default.aspx, except without the Code-Behind Processing

/MasterPage.master

<%@ Master Language="VB" CodeFile="MasterPage.master.vb" Inherits="MasterPage" %>

Ā 

Ā 

Ā 

/MasterPage.master.vb

Partial Class MasterPage
Inherits System.Web.UI.MasterPage

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'Enable GZip Encoding for this page
If Common.IsGZipEnabled() Then
Dim accEncoding As String
accEncoding = Context.Request.Headers("Accept-Encoding")
If accEncoding.Contains("gzip") Then
Response.Filter = New System.IO.Compression.GZipStream(Response.Filter, System.IO.Compression.CompressionMode.Compress)
Response.AppendHeader("Content-Encoding", "gzip")
Else
Response.Filter = New System.IO.Compression.DeflateStream(Response.Filter, System.IO.Compression.CompressionMode.Compress)
Response.AppendHeader("Content-Encoding", "deflate")
End If
End If
'Cache our response in the browser
Common.CacheIt()
'Concatenate and Minify our Scripts and Stylesheets
Response.Filter = New ZipCM.Compressor(Response.Filter)
End Sub
End Class

/Script.aspx

<%@ Page Language="VB" %>
<%
'Enable GZip Encoding
If Common.IsGZipEnabled() Then
Dim accEncoding As String
accEncoding = Context.Request.Headers("Accept-Encoding")
If accEncoding.Contains("gzip") Then
Context.Response.Filter = New System.IO.Compression.GZipStream(Context.Response.Filter, System.IO.Compression.CompressionMode.Compress)
Context.Response.AppendHeader("Content-Encoding", "gzip")
Else
Context.Response.Filter = New System.IO.Compression.DeflateStream(Context.Response.Filter, System.IO.Compression.CompressionMode.Compress)
Context.Response.AppendHeader("Content-Encoding", "deflate")
End If
End If
'Cache our response in the browser
Common.CacheIt()
'Set the content type to output true javascript
Response.ContentType = "text/javascript"
If Not (Cache("JS") Is Nothing) Then
'write out the javascript string from our cache
Response.Write(Cache("JS").ToString())
End If
%>

/Style.aspx

<%@ Page Language="VB" %>
<%
'Enable GZip Encoding
If Common.IsGZipEnabled() Then
Dim accEncoding As String
accEncoding = Context.Request.Headers("Accept-Encoding")
If accEncoding.Contains("gzip") Then
Context.Response.Filter = New System.IO.Compression.GZipStream(Context.Response.Filter, System.IO.Compression.CompressionMode.Compress)
Context.Response.AppendHeader("Content-Encoding", "gzip")
Else
Context.Response.Filter = New System.IO.Compression.DeflateStream(Context.Response.Filter, System.IO.Compression.CompressionMode.Compress)
Context.Response.AppendHeader("Content-Encoding", "deflate")
End If
End If
'Cache our response in the browser
Common.CacheIt()
'Set the content type to output true CSS
Response.ContentType = "text/css"
If Not (Cache("CSS") Is Nothing) Then
'Write it out from the cache
Response.Write(Cache("CSS").ToString())
End If
%>

Our Privacy Policy

Last Updated: June 18th, 2025

Introduction

Western Mass Hosting (“we,” “our,” or “us”) respects the privacy of all individuals and organizations that interact with our services. This Privacy Policy establishes our practices regarding the collection, use, disclosure, and protection of personal information for visitors to our website and clients utilizing our managed hosting and WordPress services. By accessing our website or engaging our services, you acknowledge that you have read and understood this policy in its entirety.

Scope and Applicability

This Privacy Policy governs our handling of information collected through our corporate website and in the course of providing managed hosting, WordPress maintenance, and development services. In accordance with global privacy regulations, we serve as a Data Controller for information related to our business operations and client relationships. When processing data on behalf of our clients through hosted services, we act as a Data Processor under applicable data protection laws.

Information We Collect

We collect various categories of information necessary to provide and improve our services. This includes personal contact and payment details provided during account registration, technical information such as IP addresses and device characteristics for security purposes, and records of communications through support channels. For clients utilizing our hosting services, we may process end-user data stored within client websites, though we do not control or monitor the collection practices of such data.

Purpose and Legal Basis for Processing

We process personal information only when we have proper justification under applicable laws. The primary legal bases for our processing activities include the necessity to fulfill contractual obligations to our clients, our legitimate business interests in maintaining and improving our services, and in limited cases, explicit consent for specific marketing communications. We maintain detailed records of processing activities to demonstrate compliance with legal requirements.

Use of Collected Information

The information we collect serves multiple business purposes. Primarily, we use this data to deliver and maintain reliable hosting services, including server provisioning, performance monitoring, and technical support. We also utilize information for business operations such as billing, customer relationship management, and service improvement initiatives. Security represents another critical use case, where we analyze data to detect and prevent fraudulent activity or unauthorized access to our systems.

Data Sharing and Third-Party Disclosures

We engage with carefully selected third-party service providers to support our operations, including cloud infrastructure providers, payment processors, and customer support platforms. These relationships are governed by strict contractual agreements that mandate appropriate data protection measures. We may disclose information when legally required to comply with court orders, government requests, or to protect our legal rights and the security of our services.

International Data Transfers

As a global service provider, we may transfer and process data in various locations worldwide. When transferring personal data originating from the European Economic Area or other regulated jurisdictions, we implement appropriate safeguards such as Standard Contractual Clauses and rely on adequacy decisions where applicable. Our subprocessors, including AWS Lightsail, maintain robust compliance certifications to ensure the protection of transferred data.

Data Retention Practices

We retain personal information only for as long as necessary to fulfill the purposes outlined in this policy. Client account information is typically maintained for five years following service termination to comply with legal and financial reporting obligations. Backup data associated with hosting services is automatically purged after thirty days, as specified in our Terms of Service. For data processed on behalf of clients, retention periods are determined by the respective client’s policies and instructions.

Security Measures

We implement comprehensive technical and organizational security measures to protect personal information against unauthorized access, alteration, or destruction. Our security program includes network encryption protocols, regular vulnerability assessments, strict access controls, and employee training on data protection best practices. We maintain incident response procedures to address potential security breaches and will notify affected parties where required by law.

Individual Rights

Individuals whose personal data we process may exercise certain rights under applicable privacy laws. These rights may include requesting access to their information, seeking correction of inaccurate data, requesting deletion under specific circumstances, and objecting to particular processing activities. We have established procedures to handle such requests in accordance with legal requirements, typically responding within thirty days of receipt. Requests should be submitted to our designated Data Protection Officer through the contact information provided in this policy.

Cookies and Tracking Technologies

Our website employs various technologies to enhance user experience and analyze site performance. Essential cookies are used for basic functionality and security purposes, while analytics cookies help us understand how visitors interact with our site. Marketing cookies are only deployed with explicit user consent. Visitors can manage cookie preferences through their browser settings or our cookie consent tool.

Policy Updates and Notifications

We periodically review and update this Privacy Policy to reflect changes in our practices or legal obligations. Material changes will be communicated to affected clients through email notifications at least thirty days prior to implementation. Continued use of our services following such notifications constitutes acceptance of the revised policy.

Contact Information

For questions or concerns regarding this Privacy Policy or our privacy practices, please contact our Data Protection Officer at info@westernmasshosting.com or by mail at:

Western Mass Hosting
22 Orlando. St.,
Feeding Hills, MA 01030.

We take all privacy-related inquiries seriously and will respond promptly to legitimate requests. For clients with specific data processing agreements, please reference your contract for any additional terms that may apply to our handling of your data.