stay out please
DESCRIPTION
How to use postmessage to solve cross domain communication issues. How to use the postmessage protocol inside backbone models.TRANSCRIPT
Stay Out Please
Itai Koren | July 2013
Framework Frontend Team @LivePerson
Who am I?
“Programmer - an organism that turns
coffee into software.” - Author
Unknown
Who am I?
~10 x Cups === feature
Who am I?
LivePerson Framework Frontend Team
Today’s Web Applications
Today’s web applications/platforms are
more than just websites
Today’s Web Applications
Javascript SDK’s/API’s
Today’s Web Applications
Today’s Web Applications
Today’s Web Applications
Today’s Web Applications
Today’s Web Applications
Widgets
Today’s Web Applications
Today’s Web Applications
Today’s Web Applications
“Learning JavaScript used to mean you weren't a
serious software developer.
Today, not learning Javascript means the same
thing.” - Jens Ohlig
Stay Out Please
XMLHttpRequest
api.lpprod.net
Backbone
jQuery as transport
liveperson.com
Stay Out
Please
Stay Out Please
S O P tay ut lease ame rigin olicy
Same Origin Policy
• Important security concept within modern browsers
• Originally released with Netscape Navigator 2 (March 1996)
• Permits script tags, images, css from any site (origin)
• Permits XHR only from the same site (origin)
• Prevents access to most methods and properties across different sites (origins)
Same Origin Policy
So which solution can we use?
Today’s Web Applications
“A good programmer is someone who always
looks both ways before crossing a one-way
street.” - Doug Linder
Same Origin Policy
We can always use JSONP
WOT?
JSONP
• Stands for JSON with Padding
• Script tags are exception to the Same Origin Policy
• Just loading a script with JSON data cannot help us (will be lost in the global context)
• The padding allows us to wrap the result with a global callback
• The response will be evaluated by the JavaScript interpreter and invoked
JSONP
So, we can always use JSONP
BUT…
Why not use JSONP?
• Only valid for GET requests
• Limited payload size
• Not flexible (no headers etc.)
• Insecure
• Causes IE to leak memory (most implementations)
Same Origin Policy
What about CORS?
CORS
• Stands for Cross Origin Resource Sharing
• A W3C spec that allows cross-domain communication from the browser
• Defines a way to determine whether or not to allow the cross-origin request
• Works by adding new HTTP headers
CORS
So what about CORS?
Why not use CORS?
• Only IE9+ support it natively (IE8 only via XDomainRequest)
• Requires “preflights” for requests other than GET or POST (with certain MIME types) and for JSON.
Same Origin Policy
So, we will probably have to use proxy
WAIT!!!
Stay Out Please
S O P tay riginal lease ame rigin olicy
lpAjax to the rescue
• Developed in LivePerson
• Self contained (Vanilla JS)
• Easy to use
• Used by entire LivePerson clients as a transport layer
• Supports three main transport types: XHR, JSONP
AND
postMessage
Browser to server communications
url.com
api.liveperson.com
api.liveperson.com/pm.html
lpAjax postmessage client
pm.htmlPostmessage serverxhr
lpAjax postMessage
• It works!!!
• Almost as fast as JSONP
• Can work with REST API’S
• Very small latency for first API call (iframe creation)
• Small latency for serialization of data for use with postMessage
• Beware: 401 Response Codes & failed requests issues
Browser Support
• Firefox >= 3
• Safari >= 4
• Chrome
• Opera >= 9
• IE >= 8
lpAjax postMessage
And there is even a shim/polyfill for IE7
window.name… (limited to 2MB only )
lpAjax postMessage
CoolI am convinced
BUT, how can I use it with Backbone
Backbone with lpAjax postMessage
• Backbone utilizes jQuery as a transport
• jQuery allows us to manipulate ajax transports at multiple levels
• $.ajaxPrefilters - Handle custom options/modify existing options before request is processed by $.ajax()
• $.ajaxTransport - Creates an object that handles the actual transmission of Ajax data and used by $.ajax() to issue requests
• Converters – to manipulate and parse the data returned from the response
Our custom ajaxPrefilter
// Register jQuery Ajax Prefilter that detects cross-domain requests and set the request data-type to "postmessage".$.ajaxPrefilter(function (options, originalOptions, jqXHR) { // Get our current origin var originBrowser = window.location; // Get the API url origin var originApi = document.createElement("a"); originApi.href = options.url; // Skip Same Origin API URL's if (originApi.hostname == originBrowser.hostname && originApi.port == originBrowser.port && originApi.protocol == originBrowser.protocol) { return; } // If the domains aren't the same and this isn't a jsonp request, force the data-type of the request to "postmessage". if ("jsonp" !== options.dataType.toLowerCase()) { // Redirect to our “postmessage” temporary transport type return "postmessage"; }});
Our ajaxTransport Implementation
// Create the postmessage transport handler (which will proxy the request to lpAjax) and register it for handling postmessage// (the '+' forces overriding any existing implementations for a transport).$.ajaxTransport("+postmessage", function (options, originalOptions, jqXHR) { // Remove the temporary transport dataType options.dataTypes.shift(); return { send:function (requestHeaders, done) { // Build the request object based on what jQuery created for us so far var req = $.extend({}, lpTag.taglets.lpAjax_request); req.headers = requestHeaders; req.method = originalOptions.type; req.data = originalOptions.data; req.url = options.url; // Implement the success and error handlers req.success = function (data) { handlePostMessageResponse(data, done); }; req.error = function (data) { handlePostMessageResponse(data, done); }; // Issue the request using lpAjax postMessage. lpAjax.postmessage.issueCall(req); }, abort:function () {} };}));
Implement the response handler
// Create the response handler for lpAjax to callvar handlePostMessageResponse = function (data, done) { // Do any parsing on the response if needed - Here I do nothing for simplicity // Now call the jQuery callback to return the response to jQuery handling done( data.code, // status, data.status, // nativeStatusText, data.body, // responses, data.headers // headers );};
Backbone with lpAjax postMessage
“If you can’t explain it simply, you don’t understand it well
enough.” -Leonardo Da Vinci
Thank You