Tuesday, June 23, 2009

Cross domain calls

The XMLHTTPRequest object is used to make an http request directly to a web server and load the server response data back into the scripting language. But this techniques works only when you are making a request to the same web server with in the same domain. Here is how to make a call from www.site1.com/user.html to www.site2.com/usercheck.aspx :

1. Create a web page on www.site1.com which would make a call to another server www.site2.com. Documentation available inline.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
<title>Check username availability</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Enter Username:
<!-- Check if username exists already or not -->
<input type="text" name="user" onchange="createScriptTag(this); return false;"/>
<span id="outputmessage"></span>
<input id='submit_button' name='submit_button' type='submit' value='submit' />
</div>
</form>

<script language="javascript" type="text/javascript">


//Create a script tag which loads javascript from a remote source
function createScriptTag(e){
var headElement;
var scriptTag;
var inputValue = e.value;
var url = 'http://www.site2.com/usercheck.aspx?username=' + inputValue + '&rnd=' + new Date().getTime();
headElement = document.getElementsByTagName("head").item(0);
scriptTag = document.createElement("script");
scriptTag.setAttribute("id", "user_script");
scriptTag.setAttribute("type", "text/javascript");
scriptTag.setAttribute("src", url);
headElement.appendChild(scriptTag);

// Creates issue with IE so remove the script tag later at an appropriate place
//headElement.removeChild(scriptTag);

}

//function to be executed on callback from the above request. The callback returns a response similar to "isAvailable({'status':1})"
function isAvailable(response){
if (response.status == 1){
document.getElementById("outputmessage").innerHTML = "Username is still available";
}
else {
document.getElementById("outputmessage").innerHTML = "Username already exists";
}
}

</script>
</body>
</html>

2. The www.site2.com/usercheck.aspx page accepts querystring parameters, 
implements a business logic and returns the function to be executed on client side as response.

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

public partial class keygenerator : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.ContentType = "text/plain";
if (Request.QueryString.Count > 0)
{
string username = Request.QueryString[0];

//array of names to check against
ArrayList arlExistingUsernames = new ArrayList();
arlExistingUsernames.Add("harpreet");
arlExistingUsernames.Add("singh");

if (!arlExistingUsernames.Contains(username))
{
Response.Write("isAvailable({'status':1})");
}

else
{
Response.Write("isAvailable({'status':0})");
}
}
else
{
Response.Write("isAvailable({'status':0})");
}
Response.End();
}
}

For more cross domain calling options check out:

http://snook.ca/archives/javascript/cross_domain_aj/

http://ox.no/posts/ajast-cross-domain-rest-calls-using-json-injection

http://developer.yahoo.com/javascript/howto-proxy.html


Technorati Tags: ,,

Saturday, June 20, 2009

Turbogears debugging in Eclipse

Setting up debug enabled environment for a Turbogears application in Eclipse is not really straightforward. I struggled a lot before I could set it up. Initially I wasnt even sure if I could set up break points and debug the application like in microsoft visual studio. It is very much possible although some of the things are not very obvious. Here is a list of things to check up, if you are facing problems.

1. You can install Pydev by following the instructions mentioned on pydev website. You can also have a look at this ibm site which helps you make the necessary settings in Eclipse.

2. You can patch the Decorators file from pydev blog. If you are facing any problems drop your email id I will send you the patched file.

3. Pydev gives problems with certain turbojson package versions. You may try to downgrade and install a previous version of turbojson package. Use the following command to do so:
>easy_install -U turbojson==1.1.4

>easy_install -U DecoratorTools==1.4 

4. Remember, you will still see the following error when you start the debug session.

PYDEV DEBUGGER WARNING:

sys.settrace() should not be used when the debugger is being used.

This may cause the debugger to stop working correctly.

If this is needed, please check: 

http://pydev.blogspot.com/2007/06/why-cant-pydev-debugger-work-with.html

to see how to restore the debug tracing back correctly.

Call Location:

  File "C:\Python24\lib\site-packages\decoratortools-1.4-py2.4.egg\peak\util\decorators.py", line 562, in uninstall


But, wait for a few seconds and eclipse will start the debug session after the error message.

Tuesday, June 16, 2009

Asp.Net and Python-cgi communication

Technorati Tags: ,,

I recently had my first shot at writing cgi scripts in python. It was an altogether different experience having come from a .Net and Turbogears background. I also got my hands on a Linux server. Infact, it was the first time I got to use vi, ls, cat, cp etc, since college. It is really amazing how much you can do with just the shell commands on Linux.


The Linux machine had an apache server which ran some cgi scripts to do some basic tasks like creating a new user record in the database, deleting, editing etc. I think that was the reason they did not go beyond cgi. These tasks were manually done by an admin after logging into the application. Now, these scripts were required to be made callable from an asp.net web application.


ASP.NET application <===========> Python cgi scripts
CreateDemo.aspx _____________ create_demo.cgi
(collects user info
for demo record) _____________ (requires authentication)

So, we decided to create another script, let us say 'authenticate.cgi', in which we used urllib python library to by pass the authentication on create_demo.cgi and then CreateDemo.aspx would call authenticate.cgi. Here is the new flow-

CreateDemo.aspx --> authenticate.cgi --> create_demo.cgi

This write-up addresses two key areas:
1. How to communicate with a python cgi script from an asp.net web page?

//Request url and request method

HttpWebRequest httpRequest = HttpWebRequest)WebRequest.Create("http://192.1.1.1/no_auth/authenticate.cgi?name=harpreet”);

httpRequest.KeepAlive = false;

httpRequest.Method = "GET";

//Read the web response from the URI

string strResponse = string.Empty;

using (WebResponse webResponse = (WebResponse)httpRequest.GetResponse())

{

System.Text.Encoding enc = System.Text.Encoding.GetEncoding(1252);

using (StreamReader srResponseStream = new StreamReader(webResponse.GetResponseStream(), enc))

{

//Read the response into a string or you could use XML also

strResponse = srResponseStream.ReadToEnd();

}

}

2. How to by pass basic authentication on cgi script using urllib?

#!/usr/bin/env /usr/local/bin/python

import urllib2,urllib

import sys,os,cgi,cgitb

import re,datetime

import base64

from urlparse import urlparse

def handler():

form = cgi.FieldStorage()

if form.list:

data = {}

for field in flds:

data[field]=form.getvalue(field,'')

theurl = 'http://localhost/admin/create_demo.cgi’

# you'll need to supply a protected page with your username and password

username = 'scott'

password = 'tiger' # a very bad password

req = urllib2.Request(theurl)

try:

handle = urllib2.urlopen(req, urllib.urlencode(data))

except IOError, e:

# do whatever you want to handle the exception here, 'pass' in this example

pass

else:

# If we don't fail then the page isn't protected

print "This page is not protected by any authentication."

sys.exit(1)

if not hasattr(e, 'code') or e.code != 401:

# we got an error - but not a 401 error

print "This page is not protected by any authentication."

print 'Some other reason for failure'

sys.exit(1)

authheader = e.headers['www-authenticate']

# this gets the www-authenticate line from the headers

# which would contain the authentication scheme and realm

authobj = re.compile(r'''(?:\s*www-authenticate\s*:)?\s*(\w*)\s+realm=['"]([^'"]+)['"]''',re.IGNORECASE)

# this RE is used to extract scheme and realm

matchobj = authobj.match(authheader)

# if the authheader isn't matched by the regular expression

# then there is probably some error in the header

if not matchobj:

print 'The authentication header is badly formed.'

print authheader

sys.exit(1)

scheme = matchobj.group(1)

realm = matchobj.group(2)

# check schem (this example works for basic authorization only

if scheme.lower() != 'basic':

print 'This example only works for BASIC Authorization.'

sys.exit(1)

base64string = base64.encodestring('%s:%s' % (username, password))[:-1]

authheader = "Basic %s" % base64string

req.add_header("Authorization", authheader)

try:

handle = urllib2.urlopen(req,urllib.urlencode(data))

except IOError, e:

print "The username or password is not valid."

sys.exit(1)

thepage = handle.read()

print "Content-type: text/html; charset=utf-8"

print

print thepage

handler()

Security was not really a major concern for this cgi, but an IP based security was added to limit unwanted access.

* The code snippets are not really tested but, will hopefully work :-)