Titanium MobileでDigest認証を使ってHTTP接続する

Titanium MobileでHTTPのDigest認証するためのコードのサンプルです。
簡単にコピペできるようにあえて、モジュール化もしていません。
ソース自身は、githubのhttpclient-digest.nodeを参照して、一部改変しました。

function sampleDigestAuth() {
	var xhr = Ti.Network.createHTTPClient();
	xhr.onload = function() {
		Ti.API.info("status:" + this.status);
		if (this.status == 200) {
			Ti.API.info("responseText:" + this.responseText);
			Ti.API.info("Content-Type:" + this.getResponseHeader("Content-Type"));
		}
	}
	xhr.onerror = function() {
		Ti.API.info("status:" + this.status);
		if (this.status == 401) {
			var wwwAuth = xhr.getResponseHeader('WWW-Authenticate');
			Ti.API.info("auth:" + wwwAuth);
			var authInfo = parseWWWAuth(wwwAuth);

			var user = "foo";
			var password = "password";
			var authorization = createAuthHeader("GET", authInfo.realm,
	             authInfo.nonce, authInfo.opaque, authInfo.qop,
	             user, password, "/");
	      	//Ti.API.info("h:" + h);
			xhr.open('GET', 'http://localhost:8124/');
	      	xhr.setRequestHeader("Authorization", authorization);
	      	xhr.send();
		}
	}

	xhr.open('GET', 'http://192.168.11.103:8124/');
	xhr.send();	
}

function parseWWWAuth(wwwAuth) {
  var ret = {};

  ret['realm'] = wwwAuth.match(/realm="([^"]+)"/)[1];
  ret['qop'] =  wwwAuth.match(/qop="([^"]+)"/)[1];
  ret['nonce'] = wwwAuth.match(/nonce="([^"]+)"/)[1];
  var opaqueMatch = wwwAuth.match(/opaque="([^"]+)"/);
  if (opaqueMatch) ret['opaque'] = opaqueMatch[0];
  return ret;
}

function createAuthHeader(method, realm, nonce, opaque, qop, 
                          username, password, uri) {
  var a1 = buildA1(realm, username, password)
  var ha1 = Titanium.Utils.md5HexDigest(a1)
  var a2 = buildA2(method, uri)
  var ha2 = Titanium.Utils.md5HexDigest(a2)

  var nc = formatNonceCount(1)
  var cnonce = buildCnonce();

  var response = buildResponse(ha1, ha2, nonce, nc, cnonce, qop)
  var hresp = Titanium.Utils.md5HexDigest(response)
  var ret = buildAuthHeader(username, realm, nonce, uri, cnonce, nc, qop, hresp, opaque);
  
  return ret;
}

// create client cnonce
function buildCnonce() {
  return random(12, "0123456789abcdef");
}

function random(len, source) {
  var result = '';
  var sourceLen = source.length;
  for (var i = 0; i < len; i++) {
    result += source.charAt(Math.floor(Math.random() * sourceLen));
  }
  return result;
}

function buildResponse(ha1, ha2, nonce, nc, cnonce, qop) {
  return ha1 + ":" + nonce + ":" + nc + ":" + qop + ":" + ha2
}

function buildAuthHeader(username, realm, nonce, uri, cnonce, nc, qop, 
                         hresp, opaque) {
  var ret = 
  	'Digest username="' + username +
    '", realm="' + realm +
    '", nonce="' + nonce +
    '", uri="' + uri +
    '", nc=' + nc +
    ', qop="' + qop +
    '", response="' + hresp +
    '", cnonce="' + cnonce + '"'
    '", algorithm="' + "MD5" + '"';    
  return ret;
}

function formatNonceCount(count) {
  var nc = count.toString(16);
  while(nc.length < 8) {
  	nc = "0" + nc;
  }
  return nc
}

function buildA1(realm, user, passwd) {
  return user + ":" + realm + ":" + passwd
}

function buildA2(method, uri) {
  return method + ":" + uri
}

参照