Added HttpHandler with Gzip support to Veggerby.Excel
Posted: August 15, 2007 Filed under: .NET General | Tags: ASP.NET, httphandler 1 Comment »Just published is a new version of Veggerby.Excel that includes a HttpHandler that not only spews out the headers and Excel XML body, but also supports Gzip compression to allow potentially very large Excel XML files to be compressed to a bare minimum.
The ExcelHttpHandler comes in the form on an abstract class with just on abstract method that must be overridden.
{
protected abstract void Render(HttpContext context);
}
To utilize the HttpHandler simply create a subclass of the ExcelHttpHandler class and overwrite the Render() method to include all the rendering of the Excel document (finalizing by calling the Render() method on the ExcelWorkbook for the context.Response.OutputStream). Fx.
{
protected override void Render(HttpContext context)
{
ExcelWorkbook workbook = new ExcelWorkbook();
// … create the workbook structure …
workbook.Save(context.Response.OutputStream);
}
}
The HttpHandler will automatically perform Gzip compression if the request’er supports is.
For more information and download see Veggerby.Excel
Writing a pingback server and client
Posted: August 1, 2007 Filed under: .NET General, ASP.NET, Code | Tags: ASP.NET, pingback, xml-rpc 3 Comments »If you can’t be bothered with all the blah, blah stuff at the beginning, scroll down to Pingback Server Implementation for the gooey details.
One of the nice features of the blogosphere is the interconnections between the blogs, the referencing back and forth, which means that once you find a pretty interesting topic, one can read on forever by following all the links.
A (the) problem with linking is that it is one-way, i.e. if I write a post that references a post on Scott Guthrie’s blog, then the readers on Scott’s blog is unaware that somebody else touched the same topic and hopefully added something to the topic. This one-way referencing also means that you will always (unless posts are updated) move back in time when you follow the references – what if someone discovered something very important that made the post, you were just reading, completely obsolete?!?
To help this the trackback (specs) method were invented that will cause the server with the refering post (i.e. the “new” post), send a notification to the server with the referred post. This was done by sending a simple HTTP request to the server being referred. However trackbacks is now a virtually extinct method, due to it being easily abused for spamming (the so called trackback spam).
So “Out with the old, and in with the nucleus“, and some clever souls thought out the pingback (specs), where the notifications are done by a XML-RPC request. The pingback request in itself does not prevent pingback spamming, however the specs recommend that the pingback “recipient” should retrieve the page sending the pingback and verify that it actually contains a link to the pingback target. Again this does not (theoretically) prevent spamming, but in practice it does anyway, since a pingback spammer would then have to link to all the pages being spammed.
Ok, enough with the talk, lets look at some code.
Pingback Server Implementation
The pingback server can in itself not be shown very generic, since it basically comes down to receiving a XML-RPC request with a source and target URI as parameters. What then happens with the source and target URI is then pretty application specific, but will in the common blog system be turned in to some kind of comment or reference in the comment section of the post.
For the code I am using the Cook Computing XML-RPC.NET library, since it is more or less the defacto standard for doing XML-RPC in .NET (when it comes to open source at least).
I we look at the pingback server implementation using XML-RPC.NET the server is a simple class which derives from CookComputing.XmlRpc.XmlRpcService. The XML-RPC service is fairly similary to the way one would create an ASMX or WCF service (though you can leave out the Service Contract and go straight to the implementation)
public class PingbackServer : XmlRpcService
{
//(…)
}
Similary to ASMX and WCF the methods are then declared by decorating them with an attribute (WebMethod or OperationContract), the XMLRPC.NET methods/operations/RPC’s are decorated with the XmlRpcMethod attribute.
The pingback request receives (as mentioned) 2 parameters:
- The source URI of the pingback
- The target URI of the pingback
So the source URI want to notify the target URI of its existence. The result of the pingback is a text message describing in some way what happened (successfully) or an errorcode (0 for a generic error, see specs for standard error codes).
So the pingback server class should look like the following:
public class PingbackServer : XmlRpcService
{
[XmlRpcMethod("pingback.ping")]
public object Ping(string sourceUri, string targetUri)
{
//(…)
}
}
Notice the “pingback.ping” parameter to the XmlRpcMethod attribute declaration. This parameter specifies the XML-RPC method name to execute, and the specifications requires this to be “pingback.ping” for interoperability.
The only other thing that can be done generically is to retrieve the page at “sourceUri” and search for a link to “targetUri” in the page – however this will be left as an exercise to the reader
or perhaps added later.
Besides creating the actual pingback service implementation, it must of course be “activated” as an XML-RPC service. This is done by declaring a HTTP Handler that reference this pingback server class (XmlRpcService implements the IHttpHandler interface). To do this add the following to web.config:
Of course if there are existing HTTP Handlers, this should be added to the existing <httpHandler></httpHandler> section.
This is actually not enough, because what is done now is that we have added the pingback server, but if a blogger creates a post referring a post on “our” server and sends a pingback to our post, he knows only the post URI not the pingback URI. There are two ways of doing this according to the pingback specifications, the point of both methods are that the page being “pinged” must specify where the pingback server is.
Method 1 is by using a HTTP Header (the X-Pingback header). This header must simply specify the absolute URI of the XML-RPC pingback server (in our example http://myserver/RPC2.ashx).
The way you would do this in ASP.NET is by using the Response.AddHeader() method (fx in Page_Load()):
Response.AddHeader(
“X-Pingback”,
Request.Url.GetComponents(
UriComponents.SchemeAndServer, UriFormat.Unescaped
) + Request.ApplicationPath + “/RPC2.ashx”);
Method 2 is by using a <link /> tag in the HTML <head></head> section. The format of the <link /> tag is:
Again it must be the absolute URI to the pingback server and the <link /> tag is must validate as either HTML or XHTML and be in the strict format as displayed above (including whitespace) – this is to reduce the complexity of extracting the pingback URI.Once this is done, then that’s it, you are now ready to receive pingback requests. In the next section we’ll look at sending pingback requests, i.e. the pingback client.Pingback Client ImplementationThe sending of a pingback request requires three steps:
- Determine the target URI(s) to ping
- Find the URI of the pingback server for each target URI
- Send a pingback request to the pingback server for each target URI, including the source URI (i.e. the information we actually want the target URI to know)
Step 1:
This is pretty straight forward (or maybe not). This step can be either extracting links form a text or simply having a list of links already at hand, either way it is application specific what actually happens in this step
Step 2:
This step is also pretty straight forward. It simply requires the client to send a request to the target URI and first send a HTTP request and receive the HTTP Response Headers and look for the X-Pingback header (as mentioned in the pingback server implementation).
WebRequest request = WebRequest.Create(targetUri);
HttpWebResponse response =
request.GetResponse() as HttpWebResponse;
if (response == null)
{
return null;
}
if (!string.IsNullOrEmpty(response.Headers["X-Pingback"]))
{
return response.Headers["X-Pingback"];
}
If the X-Pingback header is not available retrieve the HTTP response body and perform a search for a <link rel=”pingback” href=”(…)”/> tag, this can be done by a simple Regular Expression search.
using (TextReader reader = new StreamReader(response.GetResponseStream()))
{
string line;
while ((line = reader.ReadLine()) != null)
{
Regex pingback = new Regex(@”<link .*?rel=('|"")pingback('|"") .*?href=('|"")(?([^""]+)(‘|”").*?/>”);
Match pingbackMatch = pingback.Match(line);
if (pingbackMatch.Success)
{
return pingbackMatch.Groups["rpcuri"].Value;
}
}
}
Below are two regular expressions, that can be used, where the first one allows a little more slack when it comes to whitespace.
Tolerant regex:
@"<link .*?rel=('|"")pingback('|"") .*?href=('|"")(?<rpcuri>([^""]+)('|"").*?/>"
Strict regex:
@"<link rel=""pingback"" href=""(?<rpcuri>([^"]+)"" ?/?>"
With the pingback server URI at hand it just comes down to sending the pingback request using XML-RPC.NET.
string rpcUri = GetRpcUri(targetUri);
if (!string.IsNullOrEmpty(rpcUri))
{
IPingbackProxy proxy =
XmlRpcProxyGen.Create();
proxy.Url = rpcUri;
object response = proxy.Ping(sourceUri, targetUri);
}
Where IPingbackProxy specifies an contract for the XML-RPC service (similar to the WCF Service Contract). So here we actually need to declare a “service contract” (for the client).
public interface IPingbackProxy : IXmlRpcProxy
{
[XmlRpcMethod("pingback.ping")]
object Ping(string sourceUri, string targetUri);
}
Complete “Solution”
To avoid copy-paste I have compiled all this in a file (that should work
). Here I have created an “real” service contract/interface which the PingbackServer also implements.
Veggerby.Pingback (2007-08-06)
Remember you need XML-RPC.NET.
Update 2007-08-02
Funny thing
Mads Kristensen (author of Blogengine.NET) did a post yesterday (2007-08-01) as well about another ping mechanism. He describes how you can implement ping requests (using XML-RPC, however not using XML-RPC.NET, but simply “hardcoded”) to various ping-services like Ping-o-Matic, Feedburner and Technorati.
Update 2007-08-06
Discovered a small error in the discovery of the pingback URL using the <link /> method, where it only read the first line of the body.
Regex Email validator for .NET (RFC822 compliant)
Posted: August 1, 2007 Filed under: .NET General | Tags: regex, validation Leave a comment »A long, long time ago Cal Henderson created a nice PHP function for validating emails using a Regular Expression that is RFC822 compliant.
Now there is not much hokus-pokus here, but here is an expression that can be used with .NET
^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]| \x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22 ([^\x0d\x22\x5c\x80-\xff]|\x5c\x00-\x7f)*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40 \x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28 \x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])* \x5d))*$
For example:
Regex reg = new Regex(@”^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c\x00-\x7f)*\\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$”);
string[] emails = new string[] { “john@example.com”, “john.doe@example.com”, “john@example.c”};
foreach (string email in emails)
{
Console.WriteLine(“{0} = {1}”, email, reg.IsMatch(email));
}
However compliant this may be, it has practical flaws (as you can see from my simple test with 3 email adresses – john@example.c validates!). Therefor the following is a fairly good expression that I have used. It is definitely not perfect (does it exist at all?), it does give false positives and false negatives as well, however it is fairly well-balanced for average/day-to-day use.
^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))
([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$