Thursday 5 November 2015

Salesforce & XMLHttpRequest & Internet Explorer - Bermuda Triangle!

Möbius Strip
Have you ever found yourself walking in circles? Have you ever been stuck in an infinite loop with no exit? Feeling like you are trapped in the Bermuda Triangle? Maybe feeling like Jack Shephard in Lost unable to find a wait out off the island?

Well, I have to say that I have. And I have to admit that it's not only been once or twice...

I consider myself a frequent lost passenger walking on a Möbius Strip like the one next to these lines... Well, this is probably one of the side effects of being a developer.

That feeling of frustration when you get stuck, and you think about the same thing over and over again, when it appears even in your dreams and doesn't let you rest while you sleep. I guess you know what I'm talking about...

That situation is something I experienced a few weeks ago when I was working on one of my Salesforce projects, on an integration with ShareFile.

WHAT HAPPENED?

As part of our Salesforce integration, we were using the ShareFile REST API, and we had to send files as multipart/form-data.

The environment and requirements of the project forced us to send the files from the client side. We were therefore using JavaScript, so we were basically declaring an XMLHttpRequest object and sending the content with a POST method to the relevant endpoint.

We were happily working on Google Chrome, where everything worked successfully and our files were smoothly being sent to our endpoints. But we then moved to Internet Explorer, and that's when we encountered problems...

Yes, I know, who uses IE? Well, a lot of people do... And our clients used IE, so we had to make it work for IE10 and IE11.

HOW DID WE SOLVE IT?
 
Initially we thought that the problem had to do with CORS policies (Cross-Origin Resource Sharing), so we focused on that area for a while, but that wasn't the issue, because we were successfully connecting to our endpoints, so that wasn't the root of the problem.

Hours and days of googling and revisiting our code, checking for possible causes mistakes in our approach didn't really give us a clue...

We then started sniffing the traffic produced by the HTTP requests and responses sent by Chrome, to compare them with the HTTP requests and responses sent when using IE.

That was a critical exercise, because it really helped up find out some differences in the request headers. Our POST request was a multipart/form-data, and it needed to include a specific header, like the following:

var xhr = new XMLHttpRequest();
xhr.open("POST", "https://the_endpoint", true);
xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=the_boundary");
xhr.send(your_data);


But we found, by analysing the traffic, that this header was somehow being removed from the request, so the message was badly formed and our request wasn't valid. So, who was removing the header that we were populating in our code?

THE SOLUTION

The answer to our question was found by stripping down all the code from the page we were using. We removed all the Visualforce skeleton from the page, as well as all the libraries imported by Visualforce.

After doing this, our code started working! What we did was correct from the beginning!

Now, that was a relieve... We were getting there, and we only had to find out what was causing our HTTP header being removed. We did some research and found this question in the Salesforce Stack Exchange and this discussion in the Salesforce Developers forum. The discussions on these links led us to the right place.

We found out that the VRremote.js library imported by the Salesforce Visualforce pages overrides the default behaviour of the XMLHttpRequest object; so that the Content-Type request header was being removed from the request when running on Internet Explorer, and the request became invalid. By forcing it to use the default implementation of the object we could make it work for IE10 and IE11.

The only change we had to do to our code was forcing the XMLHttpRequest object to use the default implementation and not the overriden version from the VRemote.js library:

if(isIE)
{
    XMLHttpRequest = Sarissa.originalXMLHttpRequest;
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://the_endpoint", true);
xhr.setRequestHeader("Content-Type","multipart/form-data; boundary=the_boundary");
xhr.send(your_data);


EUREKA! Our code was ready! And after fixing this problem we starting catching up again with our sleep and the quality of our dreams!

THE TAKEAWAYS

Never give up!!

I said earlier that being in this type of situations where you can't seem to find a solution is one of the side effects of being a developer. It may be hard times when you can't see the light at the end of the tunnel, but how good is that feeling when you solve it!! It's like feeling you are Marty McFly!! The satisfaction of solving the problem leaves all the stress behind, and that's also a positive side effect of being a developer; I think that's the motivation we have for problem-solving, and it's what makes it great to be a developer!