1
votes

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?

1
Option 1. Some services have Flash security model in mind and provide the means to use their features. Read their documentation or contact their support. Option 2. If you don't need it to be a web-based application, use AIR. Desktop/mobile applications have less restrictions. Option 3. You can always resort to Your App <-> Your Server <-> Their Service model.Organis
Show some code that can be tested to re-create this error. Maybe someone can fix it.VC.One
@Garrapato, Is PHP an option to access the data and pass to AS3? Your security error is because your site is http:// but you try to load media from an https:// site. Even if you fix that (by using secure/HTTPS server), you will get the real error saying : Authorization header is not allowed in Actionscript. Use either PHP or Javascript and pass data to AS3 via external Interface..VC.One
I will test with https protocol, but according with the adobe documentation, IBM Watson Team should put an crossdomain.xml file with the rule in the root directory to permit the remote access from my domain.Garrapato
I just tried with the https protocol from garragames.com and the results are the same: <pre>[trace] Advertencia: Error al cargar el archivo de política desde watson-api-explorer.mybluemix.net/crossdomain.xml [trace] *** Violación de la seguridad Sandbox *** [trace] Se ha detenido la conexión con watson-api-explorer.mybluemix.net/text-to-speech/api/v1/voices/… - no se permite desde garragames.com/garra-x/Tick.swf </pre>Garrapato

1 Answers

0
votes

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 :

  • Make an input text box on stage, with instance name txtbox.
  • Save below code in document class called : 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>