From: <rb...@us...> - 2017-12-31 16:53:46
|
Revision: 15053 http://sourceforge.net/p/htmlunit/code/15053 Author: rbri Date: 2017-12-31 16:53:44 +0000 (Sun, 31 Dec 2017) Log Message: ----------- Invalid 'Origin' header was sent as part of XMLHttpRequest if the request url was absolute and the page contains a base tag Issue 1944 Modified Paths: -------------- trunk/htmlunit/src/changes/changes.xml trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest2Test.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2017-12-31 11:20:00 UTC (rev 15052) +++ trunk/htmlunit/src/changes/changes.xml 2017-12-31 16:53:44 UTC (rev 15053) @@ -8,6 +8,10 @@ <body> <release version="2.30" date="xx, 2018" description="Bugfixes"> + <action type="fix" dev="rbri" issue="1944"> + JavaScript: Invalid 'Origin' header was sent as part of XMLHttpRequest + if the request url was absolute and the page contains a base tag + </action> <action type="fix" dev="rbri" issue="1943"> JavaScript: CSSStyleSheet#addRule fixed error handling </action> Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java 2017-12-31 11:20:00 UTC (rev 15052) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlPage.java 2017-12-31 16:53:44 UTC (rev 15053) @@ -679,8 +679,6 @@ * @exception MalformedURLException if an error occurred when creating a URL object */ public URL getFullyQualifiedUrl(String relativeUrl) throws MalformedURLException { - final URL baseUrl = getBaseURL(); - // to handle http: and http:/ in FF (Bug #474) if (hasFeature(URL_MISSING_SLASHES)) { boolean incorrectnessNotified = false; @@ -693,7 +691,7 @@ } } - return WebClient.expandUrl(baseUrl, relativeUrl); + return WebClient.expandUrl(getBaseURL(), relativeUrl); } /** Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest.java 2017-12-31 11:20:00 UTC (rev 15052) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest.java 2017-12-31 16:53:44 UTC (rev 15053) @@ -527,8 +527,8 @@ containingPage_ = (HtmlPage) getWindow().getWebWindow().getEnclosedPage(); try { + final URL pageRequestUrl = containingPage_.getUrl(); final URL fullUrl = containingPage_.getFullyQualifiedUrl(url); - final URL originUrl = containingPage_.getFullyQualifiedUrl(""); if (!isAllowCrossDomainsFor(fullUrl)) { throw Context.reportRuntimeError("Access to restricted URI denied"); } @@ -537,11 +537,11 @@ request.setCharset(UTF_8); request.setAdditionalHeader(HttpHeader.REFERER, containingPage_.getUrl().toExternalForm()); - if (!isSameOrigin(originUrl, fullUrl)) { - final StringBuilder origin = new StringBuilder().append(originUrl.getProtocol()).append("://") - .append(originUrl.getHost()); - if (originUrl.getPort() != -1) { - origin.append(':').append(originUrl.getPort()); + if (!isSameOrigin(pageRequestUrl, fullUrl)) { + final StringBuilder origin = new StringBuilder().append(pageRequestUrl.getProtocol()).append("://") + .append(pageRequestUrl.getHost()); + if (pageRequestUrl.getPort() != -1) { + origin.append(':').append(pageRequestUrl.getPort()); } request.setAdditionalHeader(HttpHeader.ORIGIN, origin.toString()); } Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest2Test.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest2Test.java 2017-12-31 11:20:00 UTC (rev 15052) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XMLHttpRequest2Test.java 2017-12-31 16:53:44 UTC (rev 15053) @@ -40,12 +40,13 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.openqa.selenium.WebDriver; import com.gargoylesoftware.htmlunit.BrowserRunner; -import com.gargoylesoftware.htmlunit.HttpHeader; import com.gargoylesoftware.htmlunit.BrowserRunner.Alerts; import com.gargoylesoftware.htmlunit.BrowserRunner.BuggyWebDriver; import com.gargoylesoftware.htmlunit.BrowserRunner.NotYetImplemented; +import com.gargoylesoftware.htmlunit.HttpHeader; import com.gargoylesoftware.htmlunit.WebDriverTestCase; import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.javascript.host.xml.XMLHttpRequestTest.BasicAuthenticationServlet; @@ -642,6 +643,136 @@ loadPageWithAlerts2(html); } + /** + * Test the correct origin header. + * @throws Exception if the test fails. + */ + @Test + @Alerts({"ok", "4", "<null>"}) + public void baseUrlAbsoluteRequest() throws Exception { + final String html = "<html><head>\n" + + "<base href='" + URL_CROSS_ORIGIN_BASE + "'>\n" + + "<script>\n" + + "function test() {\n" + + " var xhr = new XMLHttpRequest();\n" + + " try {\n" + + " xhr.open('GET', '" + URL_SECOND + "', false);\n" + + " alert('ok');\n" + + " xhr.send();\n" + + " alert(xhr.readyState);\n" + + " } catch(e) { alert('exception'); }\n" + + "}\n" + + "</script>\n" + + "</head>\n" + + "<body onload='test()'></body></html>"; + + final List<NameValuePair> responseHeaders = new ArrayList<>(); + responseHeaders.add(new NameValuePair("access-control-allow-origin", "*")); + getMockWebConnection().setResponse(URL_SECOND, + "<empty/>", + 200, + "OK", + "text/xml", + UTF_8, responseHeaders); + + final WebDriver driver = loadPage2(html); + verifyAlerts(driver, Arrays.copyOfRange(getExpectedAlerts(), 0, 2)); + + final Map<String, String> lastAdditionalHeaders = getMockWebConnection().getLastAdditionalHeaders(); + String origin = lastAdditionalHeaders.get(HttpHeader.ORIGIN); + if (origin == null) { + origin = "<null>"; + } + assertEquals(getExpectedAlerts()[2], origin); + } + + /** + * Test the correct origin header. + * @throws Exception if the test fails. + */ + @Test + @Alerts(DEFAULT = {"ok", "4", "http://localhost:12345"}, + IE = {"ok", "4", "<null>"}) + public void baseUrlAbsoluteRequestOtherUrl() throws Exception { + final String html = "<html><head>\n" + + "<base href='" + URL_CROSS_ORIGIN_BASE + "'>\n" + + "<script>\n" + + "function test() {\n" + + " var xhr = new XMLHttpRequest();\n" + + " try {\n" + + " xhr.open('GET', '" + URL_CROSS_ORIGIN2 + "', false);\n" + + " alert('ok');\n" + + " xhr.send();\n" + + " alert(xhr.readyState);\n" + + " } catch(e) { alert('exception'); }\n" + + "}\n" + + "</script>\n" + + "</head>\n" + + "<body onload='test()'></body></html>"; + + final List<NameValuePair> responseHeaders = new ArrayList<>(); + responseHeaders.add(new NameValuePair("access-control-allow-origin", "*")); + getMockWebConnection().setResponse(URL_CROSS_ORIGIN2, + "<empty/>", + 200, + "OK", + "text/xml", + UTF_8, responseHeaders); + + final WebDriver driver = loadPage2(html); + verifyAlerts(driver, Arrays.copyOfRange(getExpectedAlerts(), 0, 2)); + + final Map<String, String> lastAdditionalHeaders = getMockWebConnection().getLastAdditionalHeaders(); + String origin = lastAdditionalHeaders.get(HttpHeader.ORIGIN); + if (origin == null) { + origin = "<null>"; + } + assertEquals(getExpectedAlerts()[2], origin); + } + + /** + * Test the correct origin header. + * @throws Exception if the test fails. + */ + @Test + @Alerts(DEFAULT = {"ok", "4", "http://localhost:12345"}, + IE = {"ok", "4", "<null>"}) + public void baseUrlRelativeRequest() throws Exception { + final String html = "<html><head>\n" + + "<base href='" + URL_CROSS_ORIGIN_BASE + "'>\n" + + "<script>\n" + + "function test() {\n" + + " var xhr = new XMLHttpRequest();\n" + + " try {\n" + + " xhr.open('GET', 'corsAllowAll', false);\n" + + " alert('ok');\n" + + " xhr.send();\n" + + " alert(xhr.readyState);\n" + + " } catch(e) { alert('exception ' + e); }\n" + + "}\n" + + "</script>\n" + + "</head>\n" + + "<body onload='test()'></body></html>"; + + final List<NameValuePair> responseHeaders = new ArrayList<>(); + responseHeaders.add(new NameValuePair("access-control-allow-origin", "*")); + getMockWebConnection().setResponse(new URL(URL_CROSS_ORIGIN_BASE, "/corsAllowAll"), + "<empty/>", + 200, + "OK", + "text/xml", + UTF_8, responseHeaders); + final WebDriver driver = loadPage2(html); + verifyAlerts(driver, Arrays.copyOfRange(getExpectedAlerts(), 0, 2)); + + final Map<String, String> lastAdditionalHeaders = getMockWebConnection().getLastAdditionalHeaders(); + String origin = lastAdditionalHeaders.get(HttpHeader.ORIGIN); + if (origin == null) { + origin = "<null>"; + } + assertEquals(getExpectedAlerts()[2], origin); + } + @Override protected boolean needThreeConnections() { return true; |