Reputation: 11
I'm trying to access the Watson Text to Speech API thru an action script 3 flash application. As you known Adobe implement a new security features to restrict the access across domains using a mechanism that use a rules based xml configuration file (crossdomain.xml). In my case the below error is raised when the script is executed:
Source code:
package { import flash.net.URLRequest; import flash.net.URLRequestHeader; import flash.net.URLLoaderDataFormat; import flash.net.URLLoader; import flash.net.URLVariables; import flash.net.URLRequestMethod; import flash.events.Event; import flash.events.HTTPStatusEvent; import flash.events.SecurityErrorEvent; import flash.events.IOErrorEvent; public class Greeter { public function sayHello():String { var params:Object = {user:"John",password:"secret"}; var request:URLRequest = new URLRequest(); request.url = "https://watson-api-explorer.mybluemix.net/text-to-speech/api/v1/voices"; request.contentType = "application/json"; request.method = URLRequestMethod.POST; request.data = JSON.stringify(params); var contentTypeHeader:URLRequestHeader = new URLRequestHeader("Content-Type","application/json"); var acceptHeader:URLRequestHeader = new URLRequestHeader("Accept","application/json"); var formDataHeader:URLRequestHeader = new URLRequestHeader("Content-Type","application/json"); var authorizationHeader:URLRequestHeader = new URLRequestHeader("Authorization","Basic YjcxYWUwNTMtZTJmYi00ZmQzLWFiMTctOTRjYTc2MzYzYWE3OlZ5dU9VZ0w3ak1zVw=="); request.requestHeaders = [acceptHeader,formDataHeader,authorizationHeader,contentTypeHeader]; var postLoader:URLLoader = new URLLoader(); postLoader.dataFormat = URLLoaderDataFormat.BINARY; postLoader.addEventListener(Event.COMPLETE, loaderCompleteHandler); postLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler); postLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); postLoader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); try { postLoader.load(request); } catch (error:Error) { trace("Unable to load post URL"); } var greeting:String; greeting = "Prueba de conexión a Watson!"; return JSON.stringify(request.data); } private function loaderCompleteHandler(event:Event):void { trace("loaderCompleteHandler: "); } private function httpStatusHandler(event:HTTPStatusEvent):void { trace("httpStatusHandler: "); } private function securityErrorHandler(event:SecurityErrorEvent):void { trace("securityErrorHandler: " + event); } private function ioErrorHandler(event:IOErrorEvent):void { trace("ioErrorHandler: " + event); } } }
Console output:
[trace] Advertencia: Error al cargar el archivo de política desde https://watson-api-explorer.mybluemix.net/crossdomain.xml [trace] *** Violación de la seguridad Sandbox *** [trace] Se ha detenido la conexión con https://watson-api-explorer.mybluemix.net/text-to-speech/api/v1/voices - no se permite desde http://garragames.com/garra-x/Tick.swf [trace] 05:45:44 PM | err | [SecurityErrorEvent type="securityError" bubbles=false cancelable=false eventPhase=2 text="Error #2170: Security sandbox violation: http://garragames.com/garra-x/Tick.swf cannot send HTTP headers to https://watson-api-explorer.mybluemix.net/text-to-speech/api/v1/voices."] [trace] Error #2044: Unhandled securityError:. text=Error #2170: Security sandbox violation: http://garragames.com/garra-x/Tick.swf cannot send HTTP headers to https://watson-api-explorer.mybluemix.net/text-to-speech/api/v1/voices.
¿Exist another option to access the API from Action Script Flash App?
Upvotes: 1
Views: 239
Reputation: 15881
Your real question should be "how to authenticate?" with Watson API from Flash rather than how to beat that security sandbox issue of loading/decoding via URLLoader
(which has automatic cross-domain check).
You have to authenticate (log in) somehow. This seems an unlikely thing to achieve via Actionscript only. You can see a Flash Player error like :
"Authorization header cannot be sent using Actionscript"
by using URLStream
instead URLLoader
. Also URLStream
does not care about security issues. It just gets the bytes if they exist. According to this document it says Flash "Authorization" requests are allowed. Did not work for me though. Maybe it's not allowed in debugger?
Once authenticated from your URL/Domain, then also your Flash app can make any requests as normal POST
url since it's asking via same (now allowed) domain. Use URLStream
instead of URLLoader
if you want bytes since it has no cross-domain restrictions.
PS: for example, you can use a Sound
object to playback text converted into speech.
(if authenticated
, ie: you are logged in) Try :
input
text box on stage, with instance name txtbox.Main.as
(compile as Main.swf)Test the code below : (SWF result = type into textbox and press enter to hear it spoken).
package
{
import flash.display.MovieClip;
import flash.utils.*;
import flash.media.*;
import flash.net.*;
import flash.events.*;
public class Main extends MovieClip
{
public var snd_Obj: Sound = new Sound;
public var snd_Chann: SoundChannel = new SoundChannel;
public var snd_req: URLRequest = new URLRequest();
public var str_Token: String = "";
public var url_sendto_Watson: String = "";
public var str: String = "";
public var str_Voice: String = "";
public var str_mySpeech: String = "";
public var load_Token: URLLoader;
public function Main()
{
load_Token = new URLLoader();
load_Token.addEventListener(Event.COMPLETE, onTokenLoaded);
load_Token.load(new URLRequest("https://stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/text-to-speech/api"));
//# Your token as requested from :: https://stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/text-to-speech/api
//trace("Token : " + str_Token); //# To confirm it has token code
//txtbox.type = "INPUT";
txtbox.background = true;
txtbox.text = ""; //starting text
txtbox.addEventListener(TextEvent.TEXT_INPUT, text_inputCapture);
txtbox.addEventListener(KeyboardEvent.KEY_DOWN, key_handler);
addChild(txtbox);
}
function key_handler(evt:KeyboardEvent)
{
if(evt.charCode == 13) //# if ENTER key is pressed (will send text to convert to speech)
{
str_mySpeech = txtbox.text;
str_mySpeech = str_mySpeech.replace(" ", "%20");
str_Voice = "en-US_AllisonVoice"; //or your preferred voice (see:
//# Update requested URL to include your typed text
url_sendto_Watson = ""; //# reset
url_sendto_Watson = "https://stream.watsonplatform.net/text-to-speech/api/v1/synthesize?";
url_sendto_Watson += "accept=audio/mp3"; //# get as MP3 result
url_sendto_Watson += "&text=" + str_mySpeech;
url_sendto_Watson += "&voice=" + str_Voice; //# ie: "en-US_AllisonVoice"
url_sendto_Watson += "&token=" + str_Token;
//# Attempt loading
snd_req.url = url_sendto_Watson;
snd_Obj = new Sound();
snd_Obj.addEventListener(Event.COMPLETE, onSoundLoaded);
snd_Obj.load( snd_req );
txtbox.removeEventListener(KeyboardEvent.KEY_DOWN, key_handler);
}
}
public function text_inputCapture(event:TextEvent):void
{
str = txtbox.text; //# Update text to send
txtbox.addEventListener(KeyboardEvent.KEY_DOWN, key_handler);
}
function onSoundLoaded(event:Event):void
{
snd_Chann = snd_Obj.play(); //# Play returned Speech convert result
snd_Obj.removeEventListener(Event.COMPLETE, onSoundLoaded);
}
function onTokenLoaded(evt:Event):void
{ str_Token = evt.target.data; /*# get Token result */ }
} //end Class
} //end Package
This will only work when the SWF file is embedded inside an HTML page. Something like below :
<!DOCTYPE html>
<html>
<body>
<audio id="audio_watson">
<source src="http://stream.watsonplatform.net/text-to-speech/api/v1/synthesize?accept=audio/mp3&text=welcome&voice=en-US_AllisonVoice" type="audio/mpeg">
</audio>
<embed src="Main.swf" width="800" height="600">
<script>
var a = document.getElementById("audio_watson");
a.play(); //playback to trigger authentication
</script>
</body>
</html>
Upvotes: 0