Hi All
I have been trying now for a bit to get some joy connecting from my Unity Application to the Cloud Target VWS. I have written a class that does (I think) all the required hoops and jumps to properly sign the API requests as documented. I'm obviously doing something wrong since its not working! My code is loosely based on the Java code supplied in the documentation. I'm going to post my code here in the hope that someone else is interested in achieving the same thing and will see what I have missed since I now have my frustration blinkers on.
NOTE: The following class relies on UniWeb on the Unity Asset store (sorry thats a slight barrier to helping me since its $55). If you have that then you can drop this script directly onto a gameobject and fill in your access and private keys, assign a texture and then hit play.
Problems:
- For uploading Targets I get a json response ok, but I can;t get it to authenticate. eg. {"result_code":"AuthenticationFailure","transaction_id":"aa28a843f79546c7b27d222ebbada893"}
- For get requests - the first time I run one I get an empty response from VWS but a status of OK on the web request? Subsequent calls without waiting a period of time result in a "System.Net.Sockets.SocketException: Connection refused" error. Note this may be to do with UniWeb, I'm not sure.
Hopefully someone can help.
Code:
//****************************************************************//
//CloudConnector.cs //Tom Mulder. //4/2/2013 // //This code is loosely based on the java sample code provided in the vuforia VWS documentation. // //NOTE: This class references classes and functions provided by the UniWeb plugin available on the Unity Asset Store. using UnityEngine; using System.Collections; using System; using System.Text; using System.Security.Cryptography; using System.IO; //Simple class to describe a target to submit to the Cloud. public class TargetData { public TargetData(Texture2D tex, int width, string name) { SetImage(tex); this.width = width.ToString(); this.name = name; } public string image = ""; public string width = "500"; public string name = "target_tom"; public bool active_flag = true; public void SetImage(Texture2D tex) { image = System.Convert.ToBase64String(tex.EncodeToPNG()); } } // Delegate template for callback functions passed to CloudConnector.Request public delegate void CloudRequestCallback(HTTP.Response response); //Singleton Cloud Connector Class. public class CloudConnector : MonoBehaviour { private static CloudConnector instance = null; public static CloudConnector Instance {get {return instance;}} public string accessKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Assign Access Key in the inspector public string secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Assign Secret Key in the inspector private const string md5Empty = "d41d8cd98f00b204e9800998ecf8427e"; public string host = "vws.vuforia.com"; // Host public Rect buttonRect = new Rect(0,0,200,50); public Texture2D target; //Assign an RGB24 texture here public string targetName = "testTarget"; public string demoTargetID = "1b439839e42d4d2e8e3d70a82f80afec"; // Start void Start () { if(instance != null) { Destroy(this); return; } instance = this; } //Simple GUI Implementation to fire interaction with VWS void OnGUI() { if(GUI.Button (buttonRect, "Upload New Target")) { if(target) AddTarget(target); else Debug.Log ("Assign an RGB24 image to 'target' in the inspector"); } Rect b2 = buttonRect; b2.y += b2.height; if(GUI.Button (b2, "Get Summary")) GetSummary(); b2.y += b2.height; if(GUI.Button (b2, "Get List")) GetTargetList(); b2.y += b2.height; if(GUI.Button (b2, "Get Target Summary")) GetTargetSummary(demoTargetID); } //Call for adding a target to the cloud public void AddTarget(Texture2D targetTex) { //Create a Target Data Object //Conversion of the imageTarget to Base64 String is handled in the TargetData class. TargetData td = new TargetData(target, 500, "new_target"); //Create the HTTP request - Serialize the TargetData and convert to json bytes with CreateRequestData() HTTP.Request request = new HTTP.Request("POST", "https://" + host + "/targets", CreateRequestData(td)); //Start the Http Request //Headers are Assigned in the Request Coroutine //A callback is also passed to the Request Call to be called with the HTTP Response StartCoroutine(Request(request, AddTargetResponse)); } //Pass in a class object. //Out comes a Byte[] of the json serialisation of the class private byte[] CreateRequestData(object toSerialize) { //Serialize the object referenced string json = HTTP.JsonSerializer.Encode(toSerialize); #if UNITY_EDITOR Debug.Log ("json:" + json); #endif //Encode to byte[] return Encoding.UTF8.GetBytes(json); } //Callback for the AddTarget call public void AddTargetResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } /*public void UpdateTarget(Texture2D targetTex, string targetId) { } public void UpdateTargetResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } public void GetTarget(string targetID) { } public void GetTargetResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif }*/ public void GetTargetList() { HTTP.Request request = new HTTP.Request("GET", "http://" + host + "/targets"); StartCoroutine(Request(request, GetTargetListResponse)); } public void GetTargetListResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } /*public void DeleteTarget(string targetID) { } public void DeleteTargetResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif }*/ public void GetSummary() { HTTP.Request request = new HTTP.Request("GET", "http://" + host + "/summary"); StartCoroutine(Request(request, GetSummaryResponse)); } public void GetSummaryResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } public void GetTargetSummary(string targetID) { HTTP.Request request = new HTTP.Request("GET", "http://" + host + "/targets/" + targetID); StartCoroutine(Request(request, GetTargetSummaryResponse)); } public void GetTargetSummaryResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } //Make a request using an Http Request //The callback will be called once the request is fullfilled successfully private IEnumerator Request(HTTP.Request request, CloudRequestCallback callback) { //Set the headers... SetHeaders(ref request); //Execute the request request.Send(); //Wait until finished while (!request.isDone) yield return null; //Was the request successful? if(request.exception != null) { yield break; } //Success ... maybe ... callback with the response callback(request.response); } //Set the headers for the HTTP request private void SetHeaders(ref HTTP.Request req) { string hash = md5Empty; // Deault to the hash for an empty string //Create a correctly formatted date and time string string dateTimeString = DateTime.UtcNow.ToString("R"); #if UNITY_EDITOR Debug.Log (dateTimeString); #endif //Set the host header req.AddHeader("Host", host); req.AddHeader("Date", dateTimeString); if(req.method.Equals("POST")) { hash = GetMd5Hash(req.bytes); req.AddHeader("Content-Type", "application/json"); } else req.AddHeader("Content-Type", ""); string stringToSign = req.method + "\n" + hash + "\n" + req.GetHeader("Content-Type") + "\n" + req.GetHeader("Date") + "\n" + req.uri; #if UNITY_EDITOR Debug.Log ("String to sign: " + stringToSign); #endif string authorisation = "VWS " + accessKey + ":" + BuildSignature(secretKey, stringToSign); #if UNITY_EDITOR Debug.Log ("Authorization: " + authorisation); #endif req.AddHeader("Authorization", authorisation); } //Build a signature given the private key and a string to sign. private string BuildSignature(string keyString, string stringToSign) { //Convert key and data to byte[] byte[] key = Encoding.UTF8.GetBytes(keyString); byte[] data = Encoding.UTF8.GetBytes(stringToSign); //Create the encryption thingee HMACSHA1 myhmacsha1 = new HMACSHA1(key); myhmacsha1.Initialize(); MemoryStream stream = new MemoryStream(data); //Compute the sha1 hash byte[] hash = myhmacsha1.ComputeHash(stream); //Convert to Base64 string string signature = System.Convert.ToBase64String(hash); #if UNITY_EDITOR Debug.Log ("SIG: " + signature); #endif return signature; } //Get the MD5 hash string for a Byte[] static string GetMd5Hash(byte[] toHash) { byte[] data; using (MD5 md5Hash = MD5.Create()) { data = md5Hash.ComputeHash(toHash); } // compute the hash. // Create a new Stringbuilder to collect the bytes // and create a string. StringBuilder sBuilder = new StringBuilder(); // Loop through each byte of the hashed data // and format each one as a hexadecimal string. for (int i = 0; i < data.Length; i++) { sBuilder.Append(data[i].ToString("x2")); } // Return the hexadecimal string. string hash = sBuilder.ToString(); #if UNITY_EDITOR Debug.Log ("MD5Hash: " + hash); #endif return hash; } }
Hello @nicolepodach,
Super cool add for our community! Thank you!
Note that I was able to access using this link: https://github.com/taylordigital13/VuforiaTools. Yours did not work for me for whatever reason.
Looking forward to sharing this with the Vuforia team. I'll let you know if there is any internal feedback.
Thanks again!
-Vuforia Engine Support