I will do my best to share the presentation here (video if possible) but it will be all in polish (as it is polish conference).
Speaking at MTS 2010 on "Rich user interface in ASP.NET MVC applications with jQuery plugins"
In few weeks (October 5th and 6th) I will be attending the MTS 2010 conference by Microsoft. On first day (October 5th) I will be speaking on "Rich user interface in ASP.NET MVC applications with jQuery plugins". There will be a little bit of talking and a lot of samples. I'm planning to cover CascadingDropDown, jTemplates, jQuery Treeview, jqGrid and few more.
I will do my best to share the presentation here (video if possible) but it will be all in polish (as it is polish conference).
I will do my best to share the presentation here (video if possible) but it will be all in polish (as it is polish conference).
HttpHandler with cross-origin resource sharing support
Author:
Tomasz Pęczek
on Wednesday, September 8, 2010
/
Tags:
asp.net,
cors,
cross-origin resource sharing
/
Comments: (14)
Recently I was playing around with Cross-Origin Resource Sharing (you can read a W3C Working Draft for it here), which is a mechanism to enable client-side cross-origin requests. As you can see, the last working draft is from 27 July 2010, so the technology is still in development. Let's take a look, how we can use it from JavaScript:
So we know how to perform request, let's take a look underneath. What you see below, are requests from FireFox:
We now have enough knowledge to start writing our HttpHandler:
...The first condition checks for native support of CORS through XMLHttpRequest. This is the way to go for FireFox, Chrome and Safari - you don't need to write any special code, just standard XMLHttpRequest. The second condition is for Internet Explorer, which supports cross-origin requests through XDomainRequest. I didn't managed to get it to work in Opera.
try {
//Create XMLHttpRequest and assign it to 'request' variable
...
if (request) {
//Our request URL
var jsonUrl = 'http://localhost:60000/CrossOrigin';
//Check if XMLHttpRequest support CORS
if (request.withCredentials !== 'undefined') {
//If it does, prepare request
request.open('GET', jsonUrl, true);
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
request.onreadystatechange = function () {
if (request.readyState == 4) {
if (request.status == 200) {
document.getElementById('divTarget').innerHTML = request.responseText;
}
}
};
//If not, than check if XDomainRequest is available
} else if (typeof XDomainRequest != 'undefined') {
//If it is, prepare request
request = new XDomainRequest();
request.open('GET', jsonUrl);
request.onload = function () {
document.getElementById('divTarget').innerHTML = request.responseText;
};
} else {
throw (null);
}
} else {
throw (null);
}
//Send request
request.send(null);
} catch (e) {
//There is no support for CORS, use something else instead (for example JSONP)
...
}
So we know how to perform request, let's take a look underneath. What you see below, are requests from FireFox:
OPTIONSThe first request (OPTIONS) is a preflight request. Its job is to ensure that the resource is ok with the request. It uses following headers to perform check:
Connection: keep-alive
Keep-Alive: 115
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: pl,en-us;q=0.7,en;q=0.3
Host: localhost:60000
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; pl; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 ( .NET CLR 3.5.30729; .NET4.0E)
Origin: http://localhost:50000
Access-Control-Request-Method: GET
Access-Control-Request-Headers: x-requested-with
GET
Connection: keep-alive
Keep-Alive: 115
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: pl,en-us;q=0.7,en;q=0.3
Host: localhost:60000
Referer: http://localhost:50000/CORS.htm
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; pl; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 ( .NET CLR 3.5.30729; .NET4.0E)
X-Requested-With: XMLHttpRequest
Origin: http://localhost:50000
- Origin - It contains the origin of the request. You should put Access-Control-Allow-Origin header with the same value into response if you accept the origin (you can also use '*' as a value if you consider your resource public).
- Access-Control-Request-Method - The method which will be used in actual request. In response you should put Access-Control-Allow-Methods header with list of allowed methods separated by commas.
- Access-Control-Request-Headers - Comma separated list of custom headers which will be attached to request. You should put a list of headers which you accept into Access-Control-Allow-Headers header in response.
We now have enough knowledge to start writing our HttpHandler:
public class CrossOriginHandler : IHttpHandlerThis code should be easy to understand, but there are two key methods missing. Let's start with the one which puts Access-Control-Allow-Methods and Access-Control-Allow-Headers headers into response:
{
#region IHttpHandler Members
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
//Clear the response (just in case)
ClearResponse(context);
//Checking the method
switch (context.Request.HttpMethod.ToUpper())
{
//Cross-Origin preflight request
case "OPTIONS":
//Set allowed method and headers
SetAllowCrossSiteRequestHeaders(context);
//Set allowed origin
SetAllowCrossSiteRequestOrigin(context);
break;
//Cross-Origin actual or simple request
case "GET":
//Disable caching
SetNoCacheHeaders(context);
//Set allowed origin
SetAllowCrossSiteRequestOrigin(context);
//Generate response
context.Response.ContentType = "text/plain";
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.Write("<h1>Hello World! [powered by Cross-Origin Resource Sharing]</h1>");
break;
//We doesn't support any other methods than OPTIONS and GET
default:
context.Response.Headers.Add("Allow", "OPTIONS, GET");
context.Response.StatusCode = 405;
break;
}
context.ApplicationInstance.CompleteRequest();
}
#endregion
#region Methods
protected void ClearResponse(HttpContext context)
{
context.Response.ClearHeaders();
context.Response.ClearContent();
context.Response.Clear();
}
protected void SetNoCacheHeaders(HttpContext context)
{
context.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
context.Response.Cache.SetValidUntilExpires(false);
context.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.Cache.SetNoStore();
}
#endregion
}
private void SetAllowCrossSiteRequestHeaders(HttpContext context)Now let's add the one which deals with Origin:
{
//We allow only GET method
string requestMethod = context.Request.Headers["Access-Control-Request-Method"];
if (!String.IsNullOrEmpty(requestMethod) && requestMethod.ToUpper() == "GET")
context.Response.AppendHeader("Access-Control-Allow-Methods", "GET");
//We allow any custom headers
string requestHeaders = context.Request.Headers["Access-Control-Request-Headers"];
if (!String.IsNullOrEmpty(requestHeaders))
context.Response.AppendHeader("Access-Control-Allow-Headers", requestHeaders);
}
private void SetAllowCrossSiteRequestOrigin(HttpContext context)As you can see, the method takes the absence of Origin header in Chrome and Safari into consideration.Complete sample application can be found here, go ahead and make some use of it.
{
string origin = context.Request.Headers["Origin"];
if (!String.IsNullOrEmpty(origin))
//You can make some sophisticated checks here
context.Response.AppendHeader("Access-Control-Allow-Origin", origin);
else
//This is necessary for Chrome/Safari actual request
context.Response.AppendHeader("Access-Control-Allow-Origin", "*");
}
Attempting to retrieve a user's real IP address
Author:
Tomasz Pęczek
on Wednesday, September 1, 2010
/
Tags:
asp.net,
client ip,
http headers
/
Comments: (4)
Lately, I was in need of retrieving user's IP address. The first place I went looking was REMOTE_ADDR server variable. Unfortunately, due to all proxy servers out there, this variable can be pretty far from user's real IP address. After some googling I have put together a list of HTTP headers that might contain the real IP address:
So how about this CheckIP method? The purpose of this method is to ensure that IP address is both a valid IP and does not fall within a private network range:
Now we are missing only two things. The first one is a method which converts IP from string to long:
The second thing is an array of private network IP ranges:
Now we are good to go. Please keep in mind, that this approach doesn't guarantee that you will retrieve real IP address, as all of those headers are optional.
- CLIENT-IP
- X-FORWARDED-FOR
- X-FORWARDED
- X-CLUSTER-CLIENT-IP
- FORWARDED-FOR
- FORWARDED
public string DetermineIP(HttpContext context)
{
if (context.Request.ServerVariables.AllKeys.Contains("HTTP_CLIENT_IP") && CheckIP(context.Request.ServerVariables["HTTP_CLIENT_IP"]))
return context.Request.ServerVariables["HTTP_CLIENT_IP"];
if (context.Request.ServerVariables.AllKeys.Contains("HTTP_X_FORWARDED_FOR"))
foreach (string ip in context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].Split(','))
if (CheckIP(ip.Trim()))
return ip.Trim();
if (context.Request.ServerVariables.AllKeys.Contains("HTTP_X_FORWARDED") && CheckIP(context.Request.ServerVariables["HTTP_X_FORWARDED"]))
return context.Request.ServerVariables["HTTP_X_FORWARDED"];
if (context.Request.ServerVariables.AllKeys.Contains("HTTP_X_CLUSTER_CLIENT_IP") && CheckIP(context.Request.ServerVariables["HTTP_X_CLUSTER_CLIENT_IP"]))
return context.Request.ServerVariables["HTTP_X_CLUSTER_CLIENT_IP"];
if (context.Request.ServerVariables.AllKeys.Contains("HTTP_FORWARDED_FOR") && CheckIP(context.Request.ServerVariables["HTTP_FORWARDED_FOR"]))
return context.Request.ServerVariables["HTTP_FORWARDED_FOR"];
if (context.Request.ServerVariables.AllKeys.Contains("HTTP_FORWARDED") && CheckIP(context.Request.ServerVariables["HTTP_FORWARDED"]))
return context.Request.ServerVariables["HTTP_FORWARDED"];
return context.Request.ServerVariables["REMOTE_ADDR"];
}
So how about this CheckIP method? The purpose of this method is to ensure that IP address is both a valid IP and does not fall within a private network range:
private bool CheckIP(string ip)
{
if (!String.IsNullOrEmpty(ip))
{
long ipToLong = -1;
//Is it valid IP address
if (TryConvertIPToLong(ip, out ipToLong))
{
//Does it fall within a private network range
foreach (long[] privateIp in _privateIps)
if ((ipToLong >= privateIp[0]) && (ipToLong <= privateIp[1]))
return false;
return true;
}
else
return false;
}
else
return false;
}
Now we are missing only two things. The first one is a method which converts IP from string to long:
private long ConvertIPToLong(string ip)
{
string[] ipSplit = ip.Split('.');
return (16777216 * Convert.ToInt32(ipSplit[0]) + 65536 * Convert.ToInt32(ipSplit[1]) + 256 * Convert.ToInt32(ipSplit[2]) + Convert.ToInt32(ipSplit[3]));
}
private bool TryConvertIPToLong(string ip, out long ipToLong)
{
try
{
ipToLong = ConvertIPToLong(ip);
return true;
}
catch
{
ipToLong = -1;
return false;
}
}
The second thing is an array of private network IP ranges:
private long[][] _privateIps = new long[][] {
new long[] {ConvertIPToLong("0.0.0.0"), ConvertIPToLong("2.255.255.255")},
new long[] {ConvertIPToLong("10.0.0.0"), ConvertIPToLong("10.255.255.255")},
new long[] {ConvertIPToLong("127.0.0.0"), ConvertIPToLong("127.255.255.255")},
new long[] {ConvertIPToLong("169.254.0.0"), ConvertIPToLong("169.254.255.255")},
new long[] {ConvertIPToLong("172.16.0.0"), ConvertIPToLong("172.31.255.255")},
new long[] {ConvertIPToLong("192.0.2.0"), ConvertIPToLong("192.0.2.255")},
new long[] {ConvertIPToLong("192.168.0.0"), ConvertIPToLong("192.168.255.255")},
new long[] {ConvertIPToLong("255.255.255.0"), ConvertIPToLong("255.255.255.255")}
};Now we are good to go. Please keep in mind, that this approach doesn't guarantee that you will retrieve real IP address, as all of those headers are optional.

Email