« May 2004 | Main | July 2004 »

June 17, 2004

Inventory of changes made to 2004/06/19 dasBlog codebase

As a first step to merging some of the changes I’ve made back into the common source code, here’s an inventory of all the changes made.

I’m looking for authorization and opinions on how and how much of this to merge back.

These changes can be seen at this site.

 I attempted to use the GDN interface to send a message to the workspace owner. It seemed to hang and there was no confirmation, so I’m posting the information and sending it to Clemens by e-mail.

For anyone who’s interested, here’s a zip of the changed files, and here’s a zip of the originals.

Inventory of dasBlog source code changes:

 File

Change

BlogDataService.cs

Added 3 methods to interface and implementation class.

Part of bug fix for month containing off-by-one-day posts.

CategoryCacheEntry.cs

Splits categories on comma instead of pipe. Could split on pipe if its there, and comma if not.

DayEntry.cs

Added public bool MayContain(DateTime startUtc, DateTime endUtc)

Returns true if this DayEntry may contain an entry in the UTC time interval from startUtc to endUtc.

Entry.cs

Added atomId field, public AtomId property, IsCategory method, IsLanguage method, InitializeEntryId method, and InitializeAtomId method.

EntryBase.cs

Changed value for EntryId set by Initialize from just guid to string.Format(@"{0:yyyy\/MM\/dd}/{1}", CreatedUtc, Guid.NewGuid().ToString());

This supports the readable-URL mapping implementation. After default initialization, additional code changes call InitializeAtomId and InitializeEntryId once a Title is set and before an entry is first saved, which locks in its permanent id.

TimeZone.cs

Added a FormatAdjustedUniversalTime overload and reimplemented the original in terms of the new overload which accepts a string.Format style format string to format the whole thing (DateTime, time zone name, time zone sign, time zone hour, time zone minutes).

ActivityBox.ascx.cs

AggBugs.aspx.cs         

ClickThroughs.aspx.cs

Commentview.aspx.cs

CrosspostReferrers.aspx.cs

DeleteCommentBox.ascx.cs

DeleteEntryBox.ascx.cs

EditBlogRollBox.ascx.cs

EditContentFiltersBox.ascx.cs

EditCrosspostSites.aspx.cs

EditNavigatorLinksBox.ascx.cs

Eventlog.aspx.cs

ftb.*.aspx

Login.aspx.cs

PermaLink.aspx.cs

Referrers.aspx.cs

ArchiveMonthsList.cs

CategoryList.cs

WeblogCalendar.cs

AggregatorBugHandler.cs

cdfHandler.cs

ClickThroughHandler.cs

CrosspostReferrerHandler.cs

PingbackAPI.cs

SyndicationServiceBase.cs

SyndicationService
 Implementation.cs

TrackbackHandler.cs

UrlMapper change, 1 or more link generation related changes

AdminNavBar.ascx.cs

UrlMapper, add 7 lines to map control NavigateUrl’s to absolute urls to work with 2004/06/17/foobar style URLs.

CommentViewBox.ascx.cs

UrlMapper, 3 lines

session entryId is lower case

EditConfigBox.ascx

EditConfigBox.ascx.cs

Added checkEnableUrlRewritingWithoutExtension propery & checkbox.

UrlMapper

EditEntryBox.ascx.cs

BloggerAPI.cs

MailToWeblog.cs

Added call to InitializeAtomId, UrlMapper, many

EditNavigatorLinksBox.ascx

Changed “Blogroll Feeds” to “Add new link…”, an copy and paste bug.

FormatControl.ascx.cs

FormatPage.aspx.cs

Change from calling Sever.MapPath to calling a new SiteConfig.MapPath. This was a bug. Serve.MapPath is request relative and won’t work as desired for any pages below the root folder.

Logout.ascx.cs

Factored logout logic to a static SiteSecurity.Logout() method.

Search.ascx

Added support for Google Free Site Search service. Since this is subscription based, it isn’t portable to other users unless they register and customize some hard coded properties. Should be factored differently but can also just be omitted. No dependencies.

UrlMapper

StringTables.resx

Added “text config enable url rewriting without extensions” prompt for internationalization of edit config change.

Web.config

UrlMapper section is now a no-op. It would be simple to reinstitute it as an alternative to the optimized rewriting scheme or no-rewriting. Since all URL generation / mapping is routed through one file (2 classes), it would be easy to drop in a call to the original LinkRewriter and UrlMapper logic.

Macros.cs

UrlMapper

MailTo() – bug fix, escape apostrophe in messageSubject.

MapPath bug fix.

Added AbsoluteUri, GetRssMainUrl, GetAtomMainUrl, GetStartPageUrl (UrlMapper suppot, required)

, TraceWrite, TraceWarn (allow adding to the ASP.NET trace output), no dependencies.

, DrawArchiveMonths (compact format of links to archive months with posts), no dependencies.

, WhenFormatted(string) (calls the new TimeZone.FormatAdjustedUniversalTime method)

SharedBasePage.cs

UrlMapper

Dropped in some performance timer stuff that I have to pull out or release Kizmet.Library.Diagnostics.dll.

Modified LoadEntries to call new GetEntriesInMonthLocalTime method that fixes the off-by-one-day month view bug.

SiteConfig.cs

Added using newtelligence.DasBlog.Web.Core

Added enableUrlRewritingWithoutExtensions Boolean property.

Added a UrlMapper instance field and property.

Added a public default constructor to initialize urlMapper field.

Added static MapPath method to fix users of Server.MapPath (wrong method for the job).

UrlMapper

SiteSecurity.cs

Added static Logout method factored out of logout control.

MapPath bug fixes.

Removed fix by luke@jurasource.co.uk that’s no longer needed after MapPath bug fix.

Themes.cs

Changed replace of pipe with backslash to replace of comma to backslash. This could change again to support legacy users with pipe in their cats (as opposed to cats in their pipes ;-))

UrlMapperModule.cs

Implements an optimized mapping and centralized generation of the URL api listed in the following table. Note that the native API remains unchanged and the url’s in the table are followed by “.aspx” if the configuration doesn’t call for hiding extensions (which requires a wildcard mapping in IIS). A minor change would allow supporting entries with legacy guid only EntryId values.

Overhauled the HandleBeginRequest method and added a UrlMapper class.

Would have to restore some support for the original mapping module. Could add the new behavior as a third choice after the original mapping scheme or no mapping scheme. Right now the new mapping scheme replace the old and requires a class recompile to make changes.

Utilities.cs

Many changes. Mostly migration of functionality to the UrlMapper class now living with the UrlMapperModule.

SyndicationService
ExperimentalImplementation.cs

Switched from using a url based AtomId to using the Entry.AtomId property.

 

Mapping Name

External (virtual) Url Pattern

Internal (real) Url Pattern

 

 

 

default

 

default.aspx

default

default

default.aspx

default

default?nc=1

default_nocache.aspx

login

login

login.aspx

logout

logout

logout.aspx

search

search?q=.*

SearchView.aspx?q=.*

accessdenied

accessdenied

FormatPage.aspx?path=siteConfig/accessdenied.format.html

disclaimer

disclaimer

FormatPage.aspx?path=siteConfig/disclaimer.format.html

pageerror

pageerror

FormatPage.aspx?path=siteConfig/pageerror.format.html

post

yyyy/mm/dd/[0-9a-z\-]{1,48}

PermaLink.aspx?guid=yyyy/mm/dd/[0-9a-z\-]{1,48}

postWithComments

yyyy/mm/dd/[0-9a-z\-]{1,48}?c=1

CommentView.aspx?guid=yyyy/mm/dd/[0-9a-z\-]{1,48}

day

yyyy/mm/dd

default.aspx?date=yyyy-mm-dd

month

yyyy/mm

default.aspx?month=yyyy-mm

category

category/[0-9a-z\-\., ]{1,48}

CategoryView.aspx?category=[0-9a-z\-\., ]{1,48}

rssMain

rss/main.xml

SyndicationService.asmx/GetRss

rssComment

rss/comment.xml

SyndicationService.asmx/GetCommentsRss

rssCommentPost

rss/comment/yyyy/mm/dd/[0-9a-z\-]{1,48}.xml

SyndicationService.asmx/GetEntryCommentRss?guid=yyyy/mm/dd/[0-9a-z\-]{1,48}

rssCategory

rss/category/[0-9a-z\-\., ]{1,48}.xml

SyndicationService.asmx/GetRssCategory?categoryName=[0-9a-z\-\., ]{1,48}

atomMain

atom/main.xml

SyndicationServiceExperimental.asmx/GetAtom

atomCategory

atom/category/[0-9a-z\-\., ]{1,48}.xml

SyndicationServiceExperimental.asmx/GetAtom?categoryName=[0-9a-z\-\., ]{1,48}

cdfMain

cdf/main.xml

cdf.ashx

clickThrough

ct/yyyy/mm/dd/[0-9a-z\-]{1,48}?u=.*

ct.ashx?id=yyyy/mm/dd/[0-9a-z\-]{1,48}&url=.* (maximum absolute url length is 128 for IE)

trackback

trackback/yyyy/mm/dd/[0-9a-z\-]{1,48}

Trackback.aspx?guid=yyyy/mm/dd/[0-9a-z\-]{1,48}

aggbug

aggbug/yyyy/mm/dd/[0-9a-z\-]{1,48}

aggbug.ashx?id=yyyy/mm/dd/[0-9a-z\-]{1,48}

cptrk

cptrk/yyyy/mm/dd/[0-9a-z\-]{1,48}

cptrk.ashx?id=yyyy/mm/dd/[0-9a-z\-]{1,48}

pingback

pingback

pingback.aspx

 

 

Trackback Test Post

This posts only purpose is to collect trackbacks.

Of course pingback and trackback test posts can be used interchangeably. Again, make sure you have your doctor's approval before proceeding.


http://dasblog.kizmet.org/trackback/2004/06/17/trackback-test-post

Yeah! First post on a new overhauled dasBlog engine

From the start, I wasn’t as interested in a wildly popular blog as much as a reliable publicly accessible place to share the notes I’d been accumulating for a few years. And perhaps with Weblogs.com being unceremoniously shut down, reliability and self-determination aren’t so bad.

Anyone who’s reached the point in their blogging where they’re ready to move on to a new engine knows that preserving permalinks can dominate the equation. So, being a bit anally retentive, because I clearly don’t have the volume of high quality posts and user comments to justify it, I decided to invest in the future by making the URL scheme of all public site links a top priority of the move. After looking around a bit, here’s what I wanted to end up with:

Purpose

Public URL Scheme

default

 

login

login

logout

logout

search

search?q=.*

post

yyyy/mm/dd/[0-9a-z\-]{1,48}

postWithComments

yyyy/mm/dd/[0-9a-z\-]{1,48}?c=1

day

yyyy/mm/dd

month

yyyy/mm

category

category/[0-9a-z\-\., ]{1,48}

Rss Main

rss/main.xml

Rss Comment

rss/comment.xml

Rss Comment Post

rss/comment/yyyy/mm/dd/[0-9a-z\-]{1,48}.xml

Rss Category

rss/category/[0-9a-z\-\., ]{1,48}.xml

Atom Main

atom/main.xml

Atom Category

atom/category/[0-9a-z\-\., ]{1,48}.xml

Click Through

ct/yyyy/mm/dd/[0-9a-z\-]{1,48}?u=.*

Trackback

trackback/yyyy/mm/dd/[0-9a-z\-]{1,48}

Aggregator Bug

aggbug/yyyy/mm/dd/[0-9a-z\-]{1,48}

Crosspost Tracker

cptrk/yyyy/mm/dd/[0-9a-z\-]{1,48}

pingback

pingback

 

I wanted a scheme that hid the implementation details and made it simple to figure out what post a URL relates to. If it was possible to build something today that would implement this scheme, I figured I could live with it for a long time and through multiple technology transitions. Only time will tell how truly myopic I’m being, but hell, you have to try.

WordPress was my first choice of engines based on its free, open source status, and the quality of some of the sites that use it. It was my first exposure to MySQL (which was really, really simple to get going – no wonder its doing so well) and PHP (which would have been simple if I’d remembered to remove the wildcard mapping from my IIS 5.0 server before trying to serve my first page. Overall the functionality was reasonably impressive, the standard interface was clean and appealing, but… On looking into the details of the PHP implementation it became clear that this was ASP vintage technology in an ASP.NET and JSP world. Not really un-expected, but definitely a step backwards.

The real stopping point with WP was my lack of interest in running Apache to serve it. Without Apache, there was no replacement for mod_rewrite. Despite what this post says, using PathInfo to forward information to what will always look like a PHP is a big sacrifice on the GUBUS. Without mod_rewrite, there was no grand-unified-blog-URL-scheme (Unless I wanted to write a custom ISAPI filter or something to do the work. Thanks, but no.)

It also became clear that the sites I’d liked were much more than straight WordPress. They were heavily customized and artistically templated.

So it was back to the blog engine comparison table. If I wanted to stick with a .NET codebase, the choices seemed to be Dottext or dasBlog. So I took another look at dasBlog.

dasBlog’s information schema was simpler than WP’s, but pleasantly transparent due to its use of XML files for storage. I can see a storage subsystem reimplementation on the horizon, but it makes for a quick and open prototyping platform. One of the glaring holes is the lack of a real Category entity. Event though dasBlog implements a visually attractive hierarchical category system, internally the implementation is out of gas. Because categories don’t have independent IDs and display strings, everything you might want to do with them is a compromise. A bit more work for the future.

First things first though. On to the grand-unified-BLOG-url-scheme implementation.

Having no clear idea of the level of effort (this was my learn-the-codebase project); I dove in and started making changes.

Along the way I abandoned the existing URL and link rewriting support for being non-scalable. Learned when not to use Request.MapPath (a post on that later); and about remote debugging in ASP.NET (another post); and crawled through the bowels of pingbacks and trackbacks.

By the time I finished, I’d modified every place in the code were a link was being generated to bring them all through the same centralized module that was responsible for parsing incoming URLs. Keeping things in sync is hard enough without separating where things have to be maintained.

One of the goals of the effort was to be able to run the exact same configuration on http://localhost/dasblog and http://tonesnotes.org, with or without an IIS wildcard mapping in each case. This turned out to be so cool that I ended up completely forgetting about how painful it had been to transfer a blog from one context to another. The ONLY change that has to be made is the “External Weblog URL”. Hide the ASP.NET extensions or not, run it at the root of its own IIS 6 web site or deep down on some IIS 5 developer box, it just works.

Okay, more testing is needed before I can make that claim, but there are enough cards on the table to be sure the outcome’s going to be good.

Another piece of work that came out of the effort was an extension of Scott’s import tool to consume Dottext blogs. (another post)

Time for bed now…

June 16, 2004

ASP.NET code block mystery <%= vs. <%#

It’s remarkably hard to search for syntax like “<%=” if you want to find something that speaks about the syntax rather than all the pages that use it.

I ran into a case where apparently identical code blocks in the same .ascx file were being treated differently. One was expanded and the other was treated as a literal.

The problem ended up being that a “runat=server” attribute had been added to the element with the failing code block.

I’m sure there’s a concise statement somewhere about how to escape code blocks and how they can be embedded in layers of quotations. I just haven’t found it yet.

Any pointers out there?

 

Google Free Site Search

Signed up for Google’s free site search service.

Modified their template to the following:

<form id="searchform" method="get" action="http://www.google.com/custom">

<input type="hidden" name="cof"

 value="S:http://blogs.toneengel.com/;GL:0;AH:left;LH:60;L:http://blogs.toneengel.com/skins/btone/images/sggb60.jpg“

+“;LW:60;AWFID:b764e198d2896699;">

<input type="hidden" name="domains" value="blogs.toneengel.com">

<input type="hidden" name="sitesearch" value="blogs.toneengel.com">

<p id="searchlabel"><label for="q" accesskey="4">Search this site:</label></p>

<p id="searchinput"><input type="text" id="q" name="q" size="18" maxlength="255" value="">&nbsp;
<input type="submit" value="Search"></p>

</form>

 

 

But this works much better within an ASP.NET page:

function googleSearch(searchString) {

     var cof = "S:http://blogs.toneengel.com/;GL:0;AH:left;LH:60;L:http://blogs.toneengel.com/skins/btone/images/sggb60.jpg;“+

“LW:60;AWFID:b764e198d2896699;";

     var domains = "blogs.toneengel.com";

     var sitesearch = "blogs.toneengel.com";

     var q = escape(searchString).replace(/\+/g,"%2B");

     location.href="http://www.google.com/custom?cof="+cof+"&domains="+domains+"&sitesearch="+sitesearch+"&q="+q;

}

<input type="text" id="searchString" onkeypress="if (event.keyCode == 13) googleSearch(searchString.value);" >

<input type="button" value="Google" onclick="googleSearch(searchString.value);">

 

Thanks to Mark Pilgrim for putting me on to this service.

 

June 15, 2004

Joel on Software & Interview Questions (brain teasers)

June 13, 2004

12 rules to clarify HttpRequest & Uri properties

The documentation for the HttpRequest and Uri classes was a little thin on some of their properties, so I ran some experiments to figure it out. Quite a bit of clarify emerged along with a few quirks that suggested some design rules to work around. Here’s the executive summary along with the actual experimental results.

Summary:

1.      PathInfo is path-like information following the target page and preceding the query string. It is parsed in the Url.Segments array just like the rest of the path.

2.      Path = FilePath + PathInfo.

3.      Authority = Host + Port, if Port is non-standard, otherwise Authority = Host.

4.      Fragment is generally, but not always, missing from Request.Url.Fragment.

5.      Don’t assume the last Segments array item will be the filename of the executing page because of PathInfo parsing.

6.      All non-physical paths are relative. They contain no scheme, host, or port information.

7.      PathInfo can not be included in the argument to Server.Transfer. You get an exception.

8.      Never supply Query arguments or Fragment in a Server.Transfer. You’ll get unpredictable results when combined with IHttpModule RewritePath.

9.      The PhysicalPath property corresponds to the FilePath property and NOT the CurrentExecutionFilePath.

10.  The ONLY evidence of a server transfer (Server.Transfer) is in the CurrentExecutionFilePath property.

11.  The ONLY evidence of a rewrite (Context.RewritePath) is in the RawUrl property.

12.  Always use the three argument flavor of RewritePath.

Experiment 1:

The first experiment is a fully loaded request to a file “a.aspx” in the web server’s root folder. The server is running on port 82 instead of 80 to highlight the difference between Host and Authority properties.

Request: http://localhost:82/a.aspx/path/info?query=arg#fragment

ApplicationPath

=

/

CurrentExecutionFilePath

=

/a.aspx

FilePath

=

/a.aspx

Path

=

/a.aspx/path/info

PathInfo

=

/path/info

PhysicalApplicationPath

=

c:\inetpub\wwwroot\

PhysicalPath

=

c:\inetpub\wwwroot\a.aspx

RawUrl

=

/a.aspx/path/info?query=arg

Url.Scheme

=

http

Url.Host

=

localhost

Url.Authority

=

localhost:82

Url.Segment[0]

=

/

Url.Segment[1]

=

a.aspx/

Url.Segment[2]

=

path/

Url.Segment[3]

=

info

Url.Query

=

?query=arg

Url.Fragment

=

 

Url.LocalPath

=

/a.aspx/path/info

Url.PathAndQuery

=

/a.aspx/path/info?query=arg

Url.AbsoluteUri

=

http://localhost:82/a.aspx/path/info?query=arg

 

Conclusions from Experiment 1:

1.      Path = FilePath + PathInfo

2.      There’s no trace of the fragment in any of the properties. Perhaps it is being consumed by the .NET Framework in some way.

3.      Authority = Host + Port

4.      Don’t assume the last segment is the executing file since PathInfo is parsed after it.

5.      The MSDN documentation for FilePath falsely suggests that it includes scheme and host fields.

Experiment 2:

The second experiment aims a similar request at a file “b.aspx” which immediately does a server transfer to “a.aspx”.

Request: http://localhost:82/b.aspx/path/info?query=arg#fragment

Transfer: Server.Transfer("a.aspx?st_query=st_arg#st_fragment")

ApplicationPath

=

/

CurrentExecutionFilePath

=

/a.aspx

FilePath

=

/b.aspx

Path

=

/b.aspx/path/info

PathInfo

=

/path/info

PhysicalApplicationPath

=

c:\inetpub\wwwroot\

PhysicalPath

=

c:\inetpub\wwwroot\b.aspx

RawUrl

=

/b.aspx/path/info?query=arg

Url.Scheme

=

http

Url.Host

=

localhost

Url.Authority

=

localhost:82

Url.Segment[0]

=

/

Url.Segment[1]

=

b.aspx/

Url.Segment[2]

=

path/

Url.Segment[3]

=

info

Url.Query

=

?query=arg

Url.Fragment

=

 

Url.LocalPath

=

/b.aspx/path/info

Url.PathAndQuery

=

/b.aspx/path/info?query=arg

Url.AbsoluteUri

=

http://localhost:82/b.aspx/path/info?query=arg

 

Conclusions from Experiment 2:

1.      PathInfo can not be included in the argument to Server.Transfer.

2.      The PhysicalPath property does corresponds to the FilePath property and NOT the CurrentExecutionFilePath. Use MapPath on the CurrentExecutionFilePath to find out where the actual page you’re running is located.

3.      The query string and fragment in the argument to Server.Transfer are ignored in this case (but not in all cases, see Experiment 4).

4.      The ONLY impact of a server transfer to a.aspx is in the CurrentExecutionFilePath property. In all other ways the request still looks like the original to b.aspx.

Experiment 3:

The third experiment uses an IHttpModule to do a Context.RewritePath of a similar initial request. The rewrite attempts to alter the PathInfo and Query properties of the request.

Request: http://localhost:82/c.aspx/path/info?query=arg#fragment

Rewrite: Context.RewritePath("a.aspx", "/rw_path/rw_info", "rw_query=rw_arg")

ApplicationPath

=

/

CurrentExecutionFilePath

=

/a.aspx

FilePath

=

/a.aspx

Path

=

/a.aspx/rw_path/rw_info

PathInfo

=

/rw_path/rw_info

PhysicalApplicationPath

=

c:\inetpub\wwwroot\

PhysicalPath

=

c:\inetpub\wwwroot\a.aspx

RawUrl

=

/c.aspx/path/info?query=arg

Url.Scheme

=

http

Url.Host

=

localhost

Url.Authority

=

localhost:82

Url.Segment[0]

=

/

Url.Segment[1]

=

a.aspx/

Url.Segment[2]

=

rw_path/

Url.Segment[3]

=

rw_info

Url.Query

=

?rw_query=rw_arg

Url.Fragment

=

 

Url.LocalPath

=

/a.aspx/rw_path/rw_info

Url.PathAndQuery

=

/a.aspx/rw_path/rw_info?rw_query=rw_arg

Url.AbsoluteUri

=

http://localhost:82/a.aspx/rw_path/rw_info?rw_query=rw_arg

 

Conclusions from Experiment 3:

1.      PathInfo can not be included in the argument to Server.Transfer. An exception is thrown if it is.

2.      The RewritePath PathInfo replaces the original query’s except for in the RawUrl property.

3.      The ONLY evidence of a rewrite is in the RawUrl property.

Experiment 3a:

A modification of the third experiment does a Rewrite without PathInfo or QueryString overrides to see what is preserved from the original request.

Request: http://localhost:82/g.aspx/path/info?query=arg#fragment

Rewrite: Context.RewritePath("a.aspx")

ApplicationPath

=

/

CurrentExecutionFilePath

=

/a.aspx

FilePath

=

/a.aspx

Path

=

/a.aspx

PathInfo

=

 

PhysicalApplicationPath

=

c:\inetpub\wwwroot\

PhysicalPath

=

c:\inetpub\wwwroot\a.aspx

RawUrl

=

/g.aspx/path/info?query=arg

Url.Scheme

=

http

Url.Host

=

localhost

Url.Authority

=

localhost:82

Url.Segment[0]

=

/

Url.Segment[1]

=

a.aspx

Url.Query

=

?rw_query=rw_arg

Url.Fragment

=

 

Url.LocalPath

=

/a.aspx

Url.PathAndQuery

=

/a.aspx?query=arg

Url.AbsoluteUri

=

http://localhost:82/a.aspx?query=arg

 

Conclusions from Experiment 3a:

1.      PathInfo must be explicitly transferred from the original query in a rewrite or it will be lost.

2.      Query arguments from the original query are preserved in a rewrite.

3.      In general, always use the three argument flavor or RewritePath and think carefully about what should happen to PathInfo and Query arguments.

Experiment 4:

The fourth experiment is a composition of two and three. The original query is rewritten by an IHttpModule to a page that does a server transfer to the page that finally responds to the request.

Request: http://localhost:82/d.aspx/path/info?query=arg#fragment

Rewrite: Context.RewritePath("b.aspx" "/rw_path/rw_info", "rw_query=rw_arg")

Transfer: Server.Transfer("a.aspx?st_query=st_arg#st_fragment")

ApplicationPath

=

/

CurrentExecutionFilePath

=

/a.aspx

FilePath

=

/b.aspx

Path

=

/b.aspx/rw_path/rw_info

PathInfo

=

/rw_path/rw_info

PhysicalApplicationPath

=

c:\inetpub\wwwroot\

PhysicalPath

=

c:\inetpub\wwwroot\b.aspx

RawUrl

=

/d.aspx/path/info?query=arg

Url.Scheme

=

http

Url.Host

=

localhost

Url.Authority

=

localhost:82

Url.Segment[0]

=

/

Url.Segment[1]

=

b.aspx/

Url.Segment[2]

=

rw_path/

Url.Segment[3]

=

rw_info

Url.Query

=

?st_query=st_arg

Url.Fragment

=

#st_fragment

Url.LocalPath

=

/b.aspx/rw_path/rw_info

Url.PathAndQuery

=

/b.aspx/rw_path/rw_info?st_query=st_arg

Url.AbsoluteUri

=

http://localhost:82/b.aspx/rw_path/rw_info?st_query=st_arg#st_fragment

 

Conclusions from Experiment 4:

1.      In this case the server transfer Query arguments and Fragment were transferred to the final request. Compare this to the behavior when just a server transfer is done in which there is no effect from supplying Query arguments or Fragment with the server transfer request. This suggests strongly that you NEVER supply Query arguments or Fragment in a Server.Transfer.

Experiment 5:

The last experiment is similar to Experiment 4 except it is run in an application folder two levels removed from the root folder and each step drops down an additional folder. The original request for f.aspx is rewritten into a request for rewrite/b.aspx, which does a server transfer to transfer/a.aspx. As usual, we attempt to inject alternate PathInfo, Query, and Fragment where possible.

Request: http://localhost:82/QEs/ASP.NET%20Web%20Application/f.aspx/path/info?query=arg#fragment

Rewrite: Context.RewritePath("rewrite/b.aspx", "/rw_path/rw_info", "rw_query=rw_arg")

Transfer: Server.Transfer("transfer/a.aspx?st_query=st_arg#st_fragment")

 

ApplicationPath

=

/QEs/ASP.NET Web Application

CurrentExecutionFilePath

=

/QEs/ASP.NET Web Application/rewrite/transfer/a.aspx

FilePath

=

/QEs/ASP.NET Web Application/rewrite/b.aspx

Path

=

/QEs/ASP.NET Web Application/rewrite/b.aspx/rw_path/rw_info

PathInfo

=

/rw_path/rw_info

PhysicalApplicationPath

=

C:\Tone\kzDev\Quick Experiments\ASP.NET Web Application\

PhysicalPath

=

C:\Tone\kzDev\Quick Experiments\ASP.NET Web Application\rewrite\b.aspx

RawUrl

=

/QEs/ASP.NET Web Application/f.aspx/path/info?query=arg

Url.Scheme

=

http

Url.Host

=

localhost

Url.Authority

=

localhost:82

Url.Segment[0]

=

/

Url.Segment[1]

=

QEs/

Url.Segment[2]

=

ASP.NET%20Web%20Application/

Url.Segment[3]

=

rewrite/

Url.Segment[4]

=

b.aspx/

Url.Segment[5]

=

rw_path/

Url.Segment[6]

=

rw_info

Url.Query

=

?st_query=st_arg

Url.Fragment

=

#st_fragment

Url.LocalPath

=

/QEs/ASP.NET%20Web%20Application/rewrite/b.aspx/rw_path/rw_info

Url.PathAndQuery

=

/QEs/ASP.NET%20Web%20Application/rewrite/b.aspx/rw_path/rw_info?st_query=st_arg

Url.AbsoluteUri

=

http://localhost:82/QEs/ASP.NET%20Web%20Application/rewrite/b.aspx/rw_path/rw_info?st_query=st_arg#st_fragment

 

No surprises here.