Scott Hanselman

Squeezing the most out of IIS7 Media Bit Rate Throttling

March 21, '08 Comments [5] Posted in HttpHandler | HttpModule | IIS | Podcast
Sponsored By

image ScottGu blogged about the IIS 7.0 Bit Rate Throttling Module being released. Since IIS 7.0 is totally modular (finally!) I think you'll see lots of increasingly useful modules coming out from the team. I know a couple of cool people over there so I get a little insight into what's coming soon.

Since I'm into podcasting some, I started bugging a PM in the IIS team about the Bit Rate stuff in order to dig deeper into what's going on and how I can exploit it. The next step will be to convince Carl, my producer, that we need to get on this stuff.

His Gu-ness has a better description of Bit Rate Throttling that I could write, so here's the meat of it:

Progressive Download Scenario

In a progressive download scenario a client (like Flash or Silverlight) downloads a video directly off of a web-server, and begins playing it once enough video is downloaded for it to play smoothly.

The benefit of using a progressive download approach is that it is super easy to setup on a web-server. Just copy/ftp a video up to a web-server, obtain a URL to it, and you can wire it up to a video client player. It doesn't require any custom web-server configuration, nor require a streaming server to be installed, in order to enable.

The downside of using a progressive video download approach, though, is that web-servers are by default designed to download files as fast as possible.  So when a user clicks to watch a video on your site, your web-server will attempt to transmit it to the client as fast as possible.  This is fine if the user decides to watch the entire video.  But if the user stops watching the content half way through the video (or navigates to a different page), you will have downloaded a bunch of video content that will never be watched.

If the remaining un-watched video content is several megabytes (or even tens of megabytes) in size, you will end up over time spending a lot of money on bandwidth that is not benefiting your business/site at all....

That's the problem this module tries to solve. If you think it's not a problem, talk to me or Carl. It costs a metric buttload to pay for bandwidth of my podcast and it's a harder problem to solve than you think. (Yes, every show has bittorrent as an option, but few Feed Readers use it). A surprising number of folks visit the site and just click on a show and start listening in whatever Media Player they dig.

If someone starts streaming the first 20 seconds of a 45 minute show, decides they don't like it and stops, we may have to pay for the whole 45 minutes as it might already have been downloaded! This is also good for screencasts.

The media throttling stuff looks at the file you're downloading, "bursts" the first chunk to make the "buffering" part of your listening experience as fast as possible, then it throttles the bandwidth from that point on, making sure it doesn't have to buffer again, but still saving bandwidth.

There's some technical details over here that I care about like (emphasis mine) and more technical details here:

"It's also worth noting that the throttler uses a high-performance asyncronous loop to push the data out, without tying up server threads for what can be a very long operation. For responses coming from files (like most large video files), it also does not need to read the content's of the file being sent into memory, instead just instructing http.sys to send portions of the file out to the client at a time.  Because of this, it won't significantly affect your memory usage. While this mechanism is not as efficient as http.sys's own site-wide bandwidth throttling (which cannot be used to do what we are trying to accomplish here), it is pretty much as lean as it can be."

So, I'm going to give this a try on my local machine. I'll start with a recent WMA of the show and put it in a folder under IIS.

image

Notice that this show is a WMA encoded at 32kps (for low-fi/low-bandwidth listeners) and is in http://mymachine/show. I turned on Bit Rate Throttling from the IIS Management Console.

You can set the throttle rate as a percentage of the encoded rate. I'm leaving it as 100%, so that's 100% of 32kbps after the initial burst of 20 seconds.

image

ASIDE: One of the things that is way better in IIS7 than 6 is dealing with permissions and "blocking issues." You know, when you setup a folder, configure the VDir and you get something like "permission denied" then you spend 20 minutes trying to figure out why you can't serve a freaking text file out of a folder? IIS7 has a "Test Settings" dialog like this that just saved me those 20 minutes.

image

Ok, back to business. When I hit this file I get the quick burst, then throttling hits to slow it down to a value above, but close to, the encoded rate.

image

If I try the hi-fi version of the WMA, you can see the bitrate is higher, so the throttling is smart enough to slow the bandwidth but it's always higher than the encoded rate so there's no skipping.

image

Here's what the burst followed by the throttling looks like charted.

image

Remember that this is just a stock IIS Web Server plus this new module, so we're not running Media Services or a Streaming Server or whatever.

Extending Bit Rate Throttling with Custom Code

I thought this was cool and everything, but I immediately brought up the question that some of my users download directly via RSS Readers, while others click the "play now" button and stream. I don't want to throttle the bandwidth for folks who are downloading, just those who stream. However, I'm not running a streaming server (meaning, that everything is over http://, not some mms:// funky port) so I need to differentiate between the two.

I figured there were a couple ways to handle this. I could make different VDirs in IIS mapped to the same files and have BRT (Bit Rate Throttling) turned off on one and on for the other. It'd work, but meh.

Or, I could add Windows-Media-Player/11.0.5721.5145 the end of the URL and turn BRT off for those using a custom coded IIS Module.

How to I write a module? I could make a bin folder and build a DLL, etc, but I could also just make an App_Code folder, apply the appropriate permissions...

image

...and put the source for my custom module in it. I actually wrote it in Notepad.

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;

namespace Hanselmodules
{
    public class CustomBRTModule : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.BeginRequest += new EventHandler(OnBeginRequest);
        }

        public void Dispose(){}

        public void OnBeginRequest(Object source, EventArgs e)
        {
            HttpApplication app = (HttpApplication)source;
            HttpContext context = app.Context;

            if (context.Request.QueryString["download"] == "true") //make these fast
            {
                context.Request.ServerVariables.Set("ResponseThrottler-InitialSendSize", "60000");
                context.Request.ServerVariables.Set("ResponseThrottler-Rate", "6000");
            }
        }
    }
}

Basically, I just check for ?download=true on my URL and set my initial send size and rate to really big numbers. (Certainly they could be bigger, come from config, or be calculated by me.)

From the Modules section of the IIS Manager I hit Add Module and my new module shows up. I name it and select it and it appears in the web.config in my /show folder.

 image

After installing this module if I hit: http://mymachine/show/foo.wma I get appropriate throttling, and if I hit http://mymachine/show/foo.wma?download=true I don't. Below you can see screenshots of one download finished at 9megs a second while the other is throttled at 28K/s.

image

Now, this is just one example. I might want to do something cool with JavaScript that appends the ?download if a person clicks a button, or I might want to "sniff" for browser User-Agents versus Media Players like "Windows-Media-Player/11.0.5721.5145" or iTunes/Zune or the like. It's up to me, as writing the IIS7 modules is easy.

You could even use BRT and techniques like this to throttle bandwidth to data (non-media) files, and give different chunks of bandwidth to different levels of users. My module could have checked if it was a paid user and give them preferred download speeds. Pretty sweet. Now, off to talk to Carl! :)

Related Links:

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Using ISAPI_Rewrite to canonicalize ASP.NET URLs and remove default.aspx

February 22, '07 Comments [18] Posted in ASP.NET | HttpModule | Musings | Tools
Sponsored By

In the comments of my post on Google PageRanks, Jeff Atwood says:

[The existence of] Default.aspx is another reason to consider URL rewriting. A few of my rewrite rules relative to PR:
- I don't allow links to come in as codinghorror.com, I add the www. if it is not there.
- I remove index.html if it is present

This got me thinking, as it appears that are quite a few ways to get to my home page.

You get the idea...Heck, probably just by mentioning them I'm getting in trouble, right? The URI that dare not speak its name.

Away, if we start by assuming my home page is http://www.hanselman.com/blog/ and that includes the trailing slash. We know that if my browser requests http://www.hanselman.com/blog without the slash, it'll be told by the Web Server to try it again anyway, which is just wasteful.

Apache folks have mod_rewrite and love to remind ASP.NET/IIS folks about their awesomeness. Many sites rely on mod_rewrite for certain behaviors. It's really a fundamental part of the Apache experience. The IIS story becomes better in newer versions of IIS, but the easiest and most flexible way to handle these kinds of things is ISAPI_Rewrite.

Sure, one could create an HTTP Module for ASP.NET for some of this, but at some point you'll realize that you need to catch these requests WAY earlier. Now, ISAPI_Rewrite uses Regular Expressions, and now it's time for my oft-repeated favorite RegEx joke - get ready for it:

"So you've got a problem, and you want to use Regular Expressions to solve it. Now you've got two problems."

Thanks for indulging me. Yes, writing ISAPI_Rewrite stuff is freaking voodoo and I hate it. Once you've written them, they're done. Here's mine:

[ISAPI_Rewrite]
RewriteRule /blog/default\.aspx http\://www.hanselman.com/blog/ [I,RP]

RewriteCond Host: ^hanselman\.com
RewriteRule (.*) http\://www.hanselman.com$1 [I,RP]

RewriteCond Host: ^computerzen\.com
RewriteRule (.*) http\://www.hanselman.com$1 [I,RP]

RewriteCond Host: ^www.computerzen\.com
RewriteRule (.*) http\://www.hanselman.com/blog/ [I,RP]

This rules normalize (canonicalize), to the best of my ability, all the not-really-good URLs above. It'll put everyone to http://www.hanselman.com/blog/ and even take totally lame links like http://computerzen.com/blog/GooglePageRanksConsideredSubtle.aspx and make then "correct." The "I" means "case insensitive" and the "RP" means "Redirect Permanently" - an HTTP 301. If it was just "R" it'd be a 302. When you're testing with ISAPI_Rewrite, always start with "R" to do temporary redirects, because you don't get a second chance with a 301.

So now, even if someone asks for http://www.hanselman.com/blog, they'll be told where to go(here's an HTTP conversation):

  • GET /blog HTTP/1.1
    • Heh, uh, get me /blog, m'kay?
  • HTTP/1.1 301 Moved Permanently
    Location:
    http://www.hanselman.com/blog/
  • GET /blog/ HTTP/1.1
    • Gosh, sorrey (my browser is Canadian) get me /blog/ then.

And it was Good™.

This kind of control is useful in any public facing application or web site and one should take an hour or so and really think about their website's "public face." ISAPI_Rewrite can be a powerful component as part of a larger ASP.NET solution, especially one where Google Ranks do matter and hackable or "pretty" URLs are highly valued.

For us, in the banking industry, having nice URLs like http://www.foobank.com/banking/ or http://mobile.foobank.com makes everyone happy.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

SOLVED: How to Force IIS to load a certain version of the .NET CLR

August 30, '06 Comments [4] Posted in ASP.NET | HttpModule | Web Services
Sponsored By

Micky McQuade turned me on to some code (from MSPSS) to solve my How to FORCE IIS to load a certain version of the CLR/.NET Framework. Greg Menounos also mentioned this solution in the comments.

PROBLEM: As discussed before, you can't use requiredRuntime or any .config changes (any that you ought to) to influence what version of the .NET Framework gets loaded into the IIS worker process. If you are using a .NET object as a COM object within Classic ASP, you'll always get the very latest .NET Framework installed on the machine. The values associated with the (fake) COM Object in the Registry are ignored. NOTE, this problem in with IIS/ASP/ASP.NET. If you're doing other things like calling .NET objects from VB6 Clients or something like that, you can always create a .exe.config and influence things with requiredRuntime like I talked about here.

SOLUTION: The solution is a clever hack. What's the first opportunity to "jump in" and affect IIS? When the ISAPI Filters are loaded. Which ISAPI Filter method is called only once? Why GetFilterVersion(), in fact.

This is one of those 'slap your forehead' solutions because it makes sense when you hear it, but it sounds SO tedious when you come up with it and have to actually sully your nails with C++ again. Sigh, I coded in C++ for 8 years and now not only have I forgotten my ninja skills but I feel slightly dirty when I'm back in there. 

DISCLAIMER: This code of course assumes that there isn't some other higher-priority ISAPI filter doing crazy stuff in .NET. Unlikely, but worth mentioning. It's also totally unsupported, and you didn't get it here. In fact, you don't know where you got it. Who is this? What are you doing here? Move along, nothing to see here! No warranty expressed or implied as I didn't write it. Don't bug Micky or ask MS about it.  There's also no guarantee that this, or anything like this, is a solution for your problem(s).

But it's interesting to read.

BOOL CNativeISAPIFilter::GetFilterVersion(PHTTP_FILTER_VERSION pVer)
{

    LPWSTR pszVer = L"v1.1.4322";

    //or "svr" if you're on a multiproc box and you want the server GC in this process.

    LPWSTR pszFlavor = L"wks";

    ICorRuntimeHost *pHost = NULL;

 

    HRESULT hr = CorBindToRuntimeEx(

        //version

        pszVer,

        // svr or wks

        pszFlavor,

        //domain-neutral"ness" and gc settings - see below.

        STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC,

            CLSID_CorRuntimeHost,

        IID_ICorRuntimeHost,

        (void **)&pHost);

 

    // Call default implementation for initialization

    CHttpFilter::GetFilterVersion(pVer);

 

    // Clear the flags set by base class

    pVer->dwFlags &= ~SF_NOTIFY_ORDER_MASK;

 

    // Set the flags we are interested in

    pVer->dwFlags |= SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT |
          SF_NOTIFY_END_OF_NET_SESSION | SF_NOTIFY_END_OF_REQUEST;

 

    // Set Priority

    pVer->dwFlags |= SF_NOTIFY_ORDER_HIGH;

 

    // Load description string

    TCHAR sz[SF_MAX_FILTER_DESC_LEN+1];

 

    ISAPIVERIFY(::LoadString(AfxGetResourceHandle(),

    IDS_FILTER, sz, SF_MAX_FILTER_DESC_LEN));

    _tcscpy(pVer->lpszFilterDesc, sz);

 

    return TRUE;

}

File Attachment: NativeISAPI.zip (558 KB)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Looking for Senior Software Developer at Corillian

May 17, '06 Comments [19] Posted in ASP.NET | XML | HttpHandler | HttpModule | Tools
Sponsored By

We are looking to add a Senior Software Developer to our Consumer Banking team here at Corillian.  Are you "just the right person"?  If you are, then you fit the following description:

You know web development inside and out and can explain the HTTP protocol and HTML to your mother.

You have deep expertise with development using Microsoft ASP.NET.  You know the page event model, the control event model and the difference between a HTTPModule and a HTTPHandler and when each makes sense.

You understand how DOM and SAX parsers function, how XML schema works and how it is fundamentally different than database schema.

You know that TDD really means "write the test first"
 
You know how to read code, not just write code.

You are prepared to do what it takes to deliver value to the customer.

You work with others in an environment that encourages new ideas and improvement.

You have experience using a source control tool other than VSS.

You are willing to live in Portland, OR.  (To be a part of our team, you have to be here.  No telecommuting.)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

ASP.NET MVP Hacks

April 30, '06 Comments [5] Posted in ASP.NET | TechEd | ViewState | HttpHandler | HttpModule
Sponsored By

0764597663.01._SCLZZZZZZZ_V52483501_David Yack got a bunch of folks together and assembled ASP.NET 2.0 MVP Hacks from Wrox. Take a look at the picture on the right, totally looks like we're all in the same room with our twelve-sided dice, no? ;)

Seriously though, it's a good collection of "messing with ASP" hacks. I did the sections on HttpHandlers/HttpModules and the ViewState hacks. Some of the content came from my blog. I also pulled in content, with permission and attribution from a number of other smart ASP.NET blogs. Most hacks work on both ASP.NET 1.x and 2.0 as well.

A book with a Brady Bunch cover full of nerds, yes.

A book with a lot of slick hacks, totally.

Big ups and congrats to David for pulling it all together, he's really been working hard on this book and I'm happy to have been involved along with the other (very seasoned) authors David Yack, Joe Mayo, Fredrik Normén, Dan Wahlin, J. Ambrose Little, and Jonathan Goodyear.

ASP.NET 2.0 MVP HacksProfessional ASP.NET 2.0
Total List Price: $89.98
Buy Together Today: $57.88

If you're looking to get this and Pro ASP.NET 2.0, the total price is only $57.88 on Amazon right now as a bundle. To be clear, the content in these two books doesn't overlap. The Hacks book is focused on specific and finite tips, tricks, and undocumented features and manipulations of ASP.NET Features, while the Pro ASP.NET book starts with fundamentals and builds feature by feature to advanced topics advanced to Professional ASP.NET 2.0 developers.

 

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Page 1 of 5 in the HttpModule category Next Page

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.