Cross-origin resource sharing (CORS) does not work in Firefox 13.0.1 or 6.0.2
I have a simple Java HttpServlet and a simple JSP page. They are both served by a WebSphere Application Server at port 80 on my local host. I have created a TCP/IP Monitor at port 8081 in Eclipse IDE so as to create a second origin. The protocol output further down comes from this monitor. This should work equally well on a simple Tomcat server.
When I perform the cross-origin resource sharing test, I see that all of the correct TCP data is exchanged between Firefox and the web server (i.e. HTTP OPTIONS and its response followed by an HTTP POST and its response) but the data in the body of the POST response is never passed to the XMLHttpRequest javascript object's responseText or responseXML variables and I get a status equal to 0. If I click the button while pressing the keyboard control key then the test will work as it will not be performed as a cross-origin request.
Here are all of the files used in this test:
Servlet Cors.java
-------------------------------------------------------------------------------------- package example.cors; import java.io.IOException; import java.util.Enumeration; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class Cors */ public class Cors extends HttpServlet { private static final long serialVersionUID = 1L; private static final String APPLICATION_XML_VALUE = "application/xml"; /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); // do the same as on the post } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setBufferSize(1024); response.setContentType(APPLICATION_XML_VALUE); response.setStatus(HttpServletResponse.SC_OK); String xml="<?xml version=\"1.0\"?>\n<hello>This is a wrapped message</hello>"; response.setContentLength(xml.length()); response.getWriter().append(xml); response.getWriter().close(); } /** * @see HttpServlet#doOptions(HttpServletRequest, HttpServletResponse) */ @SuppressWarnings("unchecked") protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Enumeration<String> headers=request.getHeaders("Origin"); StringBuffer sb=new StringBuffer(); while (headers.hasMoreElements()) { String o=headers.nextElement(); if (sb.length()!=0) sb.append(", "); System.err.println("Origin= "+o); sb.append(o); } response.addHeader("Access-Control-Allow-Origin", sb.toString()); response.addHeader("Access-Control-Allow-Methods","POST, GET, OPTIONS"); sb=new StringBuffer(); headers=request.getHeaders("Access-Control-Request-Headers"); while (headers.hasMoreElements()) { String o=headers.nextElement(); if (sb.length()!=0) sb.append(", "); System.err.println("Access-Control-Request-Headers= "+o); sb.append(o); } response.addHeader("Access-Control-Allow-Headers", sb.toString().toUpperCase()); response.addHeader("Access-Control-Max-Age", Integer.toString(60*60)); // 1 hour response.addHeader("Content-Type","text/plain"); response.addHeader("Allow", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS"); response.getWriter().print(""); } } -------------------------------------------------------------------------------------- And a simple JSP page test.jsp: -------------------------------------------------------------------------------------- <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <% String url ="http://localhost:8081/cors/ping"; String url_ctrl="http://localhost/cors/ping"; %> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Test CORS</title> <script type="text/javascript"> var invocation; var method='POST'; var body = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><hello>Today</hello>"; var buttontest2_label="Direct AJAX call"; function callOtherDomain(event){ invocation = new XMLHttpRequest(); if(invocation) { var resultNode = document.getElementById("buttonResultNode"); var resultMessage = document.getElementById("buttonMessageNode"); resultNode.innerHTML = ""; document.getElementById("buttontest2").value="Waiting response..."; var url if (event.ctrlKey) url="<%=url_ctrl%>"; else url="<%=url%>"; resultMessage.innerHTML = "Sending "+method+" to URL: "+url; invocation.open(method, url, true); // invocation.withCredentials = "true"; invocation.setRequestHeader('X-PINGOTHER', 'pingpong'); invocation.setRequestHeader('Content-Type', 'application/xml'); invocation.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); invocation.onerror = function(errorObject) { display_progress(resultMessage, "***** error occured=" +errorObject); }; invocation.onreadystatechange = function() { display_progress(resultMessage, "onreadystatechange="+invocation.readyState+", status="+invocation.status+", statusText="+invocation.statusText); if(invocation.readyState == 4){ document.getElementById("buttontest2").value=buttontest2_label; display_progress(resultMessage, "responseText="+invocation.responseText); resultNode.innerHTML = "Response from web service='"+invocation.responseText+"'"; } }; invocation.send(body); } } function display_progress(node, message) { node.innerHTML = node.innerHTML + "<br>" + message; } </script> </head> <body> <p>The button will create a cross site request (Use the control key to disable this, i.e. no cross site request)</p> <p><input type="button" id="buttontest2" onclick="callOtherDomain(event)" name="buttontest2" value="Waiting for page load..."></p> <p id="buttonMessageNode"></p> <p id="buttonResultNode"></p> <script type="text/javascript"> document.getElementById("buttontest2").value=buttontest2_label; </script> </body> </html> -------------------------------------------------------------------------------------- When I click on the Direct AJAX call button, I get the following output on my page: -------------------------------------------------------------------------------------- The button will create a cross site request (Use the control key to disable this, i.e. no cross site request) Sending POST to URL: http://localhost:8081/cors/ping onreadystatechange=2, status=0, statusText= onreadystatechange=4, status=0, statusText= responseText= ***** error occured=[object ProgressEvent] Response from web service='' -------------------------------------------------------------------------------------- Here is the HTTP traffic produced: -------------------------------------------------------------------------------------- HTTP REQUEST -------------------------------------------------------------------------------------- OPTIONS /cors/ping HTTP/1.1 Host: localhost:8081 User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Origin: http://localhost Access-Control-Request-Method: POST Access-Control-Request-Headers: content-type,x-pingother,x-requested-with Pragma: no-cache Cache-Control: no-cache POST /cors/ping HTTP/1.1 Host: localhost:8081 User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive X-PINGOTHER: pingpong Content-Type: application/xml; charset=UTF-8 X-Requested-With: XMLHttpRequest Referer: http://localhost/cors/client/test.jsp Content-Length: 75 Origin: http://localhost Pragma: no-cache Cache-Control: no-cache <?xml version="1.0" encoding="UTF-8" standalone="yes"?><hello>Today</hello> -------------------------------------------------------------------------------------- HTTP RESPONSE -------------------------------------------------------------------------------------- HTTP/1.1 200 OK Access-Control-Allow-Origin: http://localhost Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: CONTENT-TYPE,X-PINGOTHER,X-REQUESTED-WITH Access-Control-Max-Age: 3600 Content-Type: text/plain;charset=ISO-8859-1 Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS Content-Language: en-CA Content-Length: 0 Date: Wed, 11 Jul 2012 17:50:10 GMT Server: WebSphere Application Server/7.0 HTTP/1.1 200 OK Content-Type: application/xml Content-Length: 62 Content-Language: en-CA Date: Wed, 11 Jul 2012 17:50:10 GMT Server: WebSphere Application Server/7.0 <?xml version="1.0"?> <hello>This is a wrapped message</hello> --------------------------------------------------------------------------------------
Edeziri
All Replies (6)
Are there any relevant errors in Firefox's Error Console (Ctrl_Shift+j)?
Does it make any difference if you change this line to * (i.e., match any origin):
response.addHeader("Access-Control-Allow-Origin", "*");
Are you able to test on any other host, in case localhost is a special case?
No errors in error console. No effect using *. I tried using the dns name of my localhost both in the Firefox URL and in the javascript and I get exactly the same. I have spent a huge amount of time looking into this issue.
One thing I noticed is that if I use the examples on the internet (http://arunranga.com/examples/access-control/preflightInvocation.html or http://saltybeagle.com/cors/) they work in the same browser. These examples however, are accessed through HTTP proxies.
I am wondering if the issue has to do with using the same hostname just with different ports.
Jefferson, were you able to get my example to run. All you need is tomcat to run this test. Would you mind (or anyone else) mind trying my example out to see if you get the same result?
It's unlikely I can replicate your environment in the near future (multiple ports on localhost; I don't think the server technology -- JSP, PHP, ASP -- is critical).
I agree that the server technology is not important. However, my example is self contained. If you have eclipse you can use the tcp/ip monitor to provide the second port. Alternatively, you can configure tomcat to listen on two ports. Regardless of if you use my sample servlet or some other server technology, could you try to see if you can reproduce this?
Also, is there a way to report this as a Firefox bug which I strongly believe it is? If have done more tests using different configuration and I am unable to get it to work.
Simpler test page (PHP): http://www.jeffersonscher.com/forumshots/corstest.html
I had to return the Access-Control-Allow-Origin header in the second response in order to get it to work. Try adding that to your doPost function (method?) and see whether that fixes it.