Test: Cross Origin Manual Test

This commit adds a manual testing application for checking Cross-origin behaviour in different browsers. A README file included in this application provides details on how to run the tests.
This commit is contained in:
Andrew Hankinson committed 2014-12-09 16:07:43 -05:00
1 parent 2a16fb5e09
commit 642dc36c41
10 files changed
+2495

No files matched your search

@@ -0,0 +1,78 @@
/*
* AppController.j
* CrossOriginTest
*
* Created by You on December 5, 2014.
* Copyright 2014, Your Company All rights reserved.
*/
@import <Foundation/Foundation.j>
@import <AppKit/AppKit.j>
var corsServer = "http://localhost:8001";
@implementation AppController : CPObject
{
@outlet CPWindow theWindow;
@outlet CPButton theButton;
@outlet CPButton setsWithCredentials;
}
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
}
- (void)awakeFromCib
{
// This is called when the cib is done loading.
// You can implement this method on any object instantiated from a Cib.
// It's a useful hook for setting up current UI values, and other things.
// In this case, we want the window from Cib to become our full browser window
[theWindow setFullPlatformWindow:YES];
}
-(void)connection:(CPURLConnection)connection didReceiveResponse:(CPHTTPURLResponse)response
{
console.log("Response received");
}
-(void)connection:(CPURLConnection)connection didReceiveData:(CPString)data
{
var wc = ([[connection originalRequest] withCredentials]) ? "YES" : "NO"
console.log("CPURLConnection was sent with credentials? " + wc + " Response: " + data);
}
- (@action)stateOfWithCredentials:(id)aSender
{
console.log([setsWithCredentials state]);
}
- (@action)testCappuccinoRequest:(id)aSender
{
var req = [CPURLRequest requestWithURL:[CPURL URLWithString:corsServer + @"/resp.json"]];
[req setValue:@"no-cache" forHTTPHeaderField:@"Pragma"];
[req setValue:@"no-store, no-cache, must-revalidate, post-check=0, pre-check=0" forHTTPHeaderField:@"Cache-Control"];
[req setWithCredentials:[setsWithCredentials state]];
[[CPURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}
- (@action)testNativeRequest:(id)aSender
{
var wc = ([setsWithCredentials state]) ? true : false;
var req = new XMLHttpRequest();
function reqListener ()
{
console.log("Native XHR was sent with credentials? " + wc + " Response: " + this.responseText);
}
req.onload = reqListener;
req.withCredentials = wc;
req.open("GET", corsServer + "/resp.json", true);
req.setRequestHeader("Pragma", "no-cache");
req.setRequestHeader("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
req.send();
}
@end
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Main cib file base name</key>
<string>MainMenu.cib</string>
<key>CPBundleName</key>
<string>CrossOriginTest</string>
<key>CPBundleVersion</key>
<string>1.0</string>
<key>CPHumanReadableCopyright</key>
<string>Copyright © 2014, Your Company All rights reserved.</string>
</dict>
</plist>
+184
View File
@@ -0,0 +1,184 @@
/*
* Jakefile
* CrossOriginTest
*
* Created by You on December 9, 2014.
* Copyright 2014, Your Company All rights reserved.
*/
var ENV = require("system").env,
FILE = require("file"),
JAKE = require("jake"),
task = JAKE.task,
FileList = JAKE.FileList,
app = require("cappuccino/jake").app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
OS = require("os"),
projectName = "CrossOriginTest";
app (projectName, function(task)
{
ENV["OBJJ_INCLUDE_PATHS"] = "Frameworks";
if (configuration === "Debug")
ENV["OBJJ_INCLUDE_PATHS"] = FILE.join(ENV["OBJJ_INCLUDE_PATHS"], configuration);
task.setBuildIntermediatesPath(FILE.join("Build", "CrossOriginTest.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setProductName("CrossOriginTest");
task.setIdentifier("com.yourcompany.CrossOriginTest");
task.setVersion("1.0");
task.setAuthor("Your Company");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("CrossOriginTest");
task.setSources(new FileList("**/*.j").exclude(FILE.join("Build", "**")).exclude(FILE.join("Frameworks", "Source", "**")));
task.setResources(new FileList("Resources/**"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
else
task.setCompilerFlags("-O");
});
task ("default", [projectName], function()
{
printResults(configuration);
});
task ("build", ["default"], function()
{
updateApplicationSize();
});
task ("debug", function()
{
configuration = ENV["CONFIGURATION"] = "Debug";
JAKE.subjake(["."], "build", ENV);
});
task ("release", function()
{
configuration = ENV["CONFIGURATION"] = "Release";
JAKE.subjake(["."], "build", ENV);
});
task ("run", ["debug"], function()
{
OS.system(["open", FILE.join("Build", "Debug", projectName, "index.html")]);
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", projectName, "index.html")]);
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", projectName));
OS.system(["press", "-f", FILE.join("Build", "Release", projectName), FILE.join("Build", "Deployment", projectName)]);
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Desktop", projectName));
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", projectName), FILE.join("Build", "Desktop", projectName, "CrossOriginTest.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
OS.system([FILE.join("Build", "Desktop", projectName, "CrossOriginTest.app", "Contents", "MacOS", "NativeHost"), "-i"]);
});
function printResults(configuration)
{
print("----------------------------");
print(configuration+" app built at path: "+FILE.join("Build", configuration, projectName));
print("----------------------------");
}
function updateApplicationSize()
{
print("Calculating application file sizes...");
var contents = FILE.read(FILE.join("Build", configuration, projectName, "Info.plist"), { charset:"UTF-8" }),
format = CFPropertyList.sniffedFormatOfString(contents),
plist = CFPropertyList.propertyListFromString(contents),
totalBytes = {executable:0, data:0, mhtml:0};
// Get the size of all framework executables and sprite data
var frameworksDir = "Frameworks";
if (configuration === "Debug")
frameworksDir = FILE.join(frameworksDir, "Debug");
var frameworks = FILE.list(frameworksDir);
frameworks.forEach(function(framework)
{
if (framework !== "Source")
addBundleFileSizes(FILE.join(frameworksDir, framework), totalBytes);
});
// Read in the default theme name, and attempt to get its size
var themeName = plist.valueForKey("CPDefaultTheme") || "Aristo2",
themePath = nil;
if (themeName === "Aristo" || themeName === "Aristo2")
themePath = FILE.join(frameworksDir, "AppKit", "Resources", themeName + ".blend");
else
themePath = FILE.join("Frameworks", "Resources", themeName + ".blend");
if (FILE.isDirectory(themePath))
addBundleFileSizes(themePath, totalBytes);
// Add sizes for the app
addBundleFileSizes(FILE.join("Build", configuration, projectName), totalBytes);
print("Executables: " + totalBytes.executable + ", sprite data: " + totalBytes.data + ", total: " + (totalBytes.executable + totalBytes.data));
var dict = new CFMutableDictionary();
dict.setValueForKey("executable", totalBytes.executable);
dict.setValueForKey("data", totalBytes.data);
dict.setValueForKey("mhtml", totalBytes.mhtml);
plist.setValueForKey("CPApplicationSize", dict);
FILE.write(FILE.join("Build", configuration, projectName, "Info.plist"), CFPropertyList.stringFromPropertyList(plist, format), { charset:"UTF-8" });
}
function addBundleFileSizes(bundlePath, totalBytes)
{
var bundleName = FILE.basename(bundlePath),
environment = bundleName === "Foundation" ? "Objj" : "Browser",
bundlePath = FILE.join(bundlePath, environment + ".environment");
if (FILE.isDirectory(bundlePath))
{
var filename = bundleName + ".sj",
filePath = new FILE.Path(FILE.join(bundlePath, filename));
if (filePath.exists())
totalBytes.executable += filePath.size();
filePath = new FILE.Path(FILE.join(bundlePath, "dataURLs.txt"));
if (filePath.exists())
totalBytes.data += filePath.size();
filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLData.txt"));
if (filePath.exists())
totalBytes.mhtml += filePath.size();
filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLPaths.txt"));
if (filePath.exists())
totalBytes.mhtml += filePath.size();
}
}
+45
View File
@@ -0,0 +1,45 @@
This test checks the withCredentials CORS functionality with Cappuccino.
Running the test:
You will need to start two HTTP Servers; one on localhost:8000, and one on localhost:8001.
The first:
`$> python -m SimpleHTTPServer` // starts it on 8000
In another terminal window:
`$> python cors-server.py` // starts another on 8001
Visit http://localhost:8000 in your web browser. There are two buttons and a checkbox.
One button will issue a native XMLHTTPRequest. The other will issue a CPURLConnection request. The checkbox will control whether the withCredentials option is set on both types of requests.
With the checkbox checked, you should press either of the buttons. In the terminal with the 'cors-server.py' script running you will see output that should match the following:
```
INFO:root:CORS: With Credentials
127.0.0.1 - - [05/Dec/2014 18:44:48] "GET /resp.json HTTP/1.1" 200 -
```
If you uncheck the checkbox, you should see the following:
```
INFO:root:CORS: No Credentials
127.0.0.1 - - [05/Dec/2014 18:44:56] "GET /resp.json HTTP/1.1" 200 -
```
This error message is controlled by the presence of the 'Cookies' header.
NOTE: The cors-server.py will set a cookie for you (mycookie=cappuccino!), but only after the first request. If you don't have a cookie set for localhost, the server message will show 'No Credentials' on the first request since the Cookie header is not set. Subsequent requests will behave correctly.
The browser console will also provide some status information about the request and response.
# A note about IE<sup>**</sup>
As best I can tell, IE behaves differently than all other browsers. I have tested this in Chrome and Firefox on Mac & Windows, Safari on Mac, IE11 on Windows. In this test, unchecking the 'With Credentials' will tell the browser to not send a cookie to the server if the server and client are not on the same host. However, in IE, it will pass the cookie along if the server and host are on the same top domain, but not necessarily the same host. The corollary of this is that if the two servers are on different domains, the `withCredentials` setting in IE does absolutely nothing.
To get it to work, you must instruct your users to adjust their cookie privacy settings and allow third-party cookies. This seemed to work for me, but dynamically adjusting the `withCredentials` parameter did nothing with this on -- IE always sent the cookies if it was configured to do so.
<sup>*</sup> What, you expected IE to actually work like the rest of the world?
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,44 @@
import SimpleHTTPServer
import SocketServer
import logging
import cgi
logging.basicConfig(level=logging.INFO)
PORT = 8001
class ServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def end_headers (self):
self.send_header('Set-Cookie', 'mycookie=cappuccino!')
self.send_header('Access-Control-Allow-Origin', 'http://142.157.142.237:8000')
self.send_header('Access-Control-Allow-Methods', 'GET, OPTIONS')
self.send_header("Access-Control-Allow-Headers", "X-Requested-With, If-Modified-Since, Cache-Control, Pragma")
self.send_header('Access-Control-Allow-Credentials', 'true')
SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
def do_OPTIONS(self):
logging.info("OPTIONS Request")
self.send_response(204, "No Content")
self.send_header('Access-Control-Allow-Origin', 'http://142.157.142.237:8000')
self.send_header('Access-Control-Allow-Methods', 'GET, OPTIONS')
self.send_header("Access-Control-Allow-Headers", "X-Requested-With, If-Modified-Since, Cache-Control, Pragma")
self.send_header('Access-Control-Allow-Credentials', 'true')
self.send_header("Access-Control-Max-Age", 10)
self.send_header("content-length", 0)
def do_GET(self):
try:
self.headers['Cookie']
logging.info("CORS: With Credentials")
logging.info(self.headers['Cookie'])
except KeyError, e:
logging.info("CORS: No Credentials")
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
Handler = ServerHandler
SocketServer.TCPServer.allow_reuse_address = True
httpd = SocketServer.TCPServer(("0.0.0.0", PORT), Handler)
print "serving at port", PORT
httpd.serve_forever()
@@ -0,0 +1,191 @@
<!DOCTYPE html>
<html lang="en">
<!--
index-debug.html
CrossOriginTest
Created by You on December 5, 2014.
Copyright 2014, Your Company All rights reserved.
-->
<head>
<meta charset="utf-8">
<!--[if lte IE 8]>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1">
<![endif]-->
<!--[if gte IE 9]>
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<![endif]-->
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="apple-touch-icon" href="Resources/icon.png">
<link rel="apple-touch-startup-image" href="Resources/default.png">
<title>TestIEWithCredentials</title>
<!-- Custom javascript goes here -->
<!-- End custom javascript -->
<script type="text/javascript">
OBJJ_MAIN_FILE = "main.j";
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks"];
var progressBar = null;
OBJJ_PROGRESS_CALLBACK = function(percent, appSize, path)
{
percent = percent * 100;
if (!progressBar)
progressBar = document.getElementById("progress-bar");
if (progressBar)
progressBar.style.width = Math.min(percent, 100) + "%";
}
var loadingHTML =
'<div id="loading">' +
' <div id="loading-text">Loading...</div>' +
' <div id="progress-indicator">' +
' <span id="progress-bar" style="width:0%"></span>' +
' </div>' +
'</div>';
</script>
<script type="text/javascript" src="Frameworks/Debug/Objective-J/Objective-J.js" charset="UTF-8"></script>
<script type="text/javascript">
objj_msgSend_reset();
// DEBUG OPTIONS:
// Uncomment to enable printing of backtraces on exceptions:
//objj_msgSend_decorate(objj_backtrace_decorator);
// Uncomment to supress exceptions that take place inside a message
//objj_msgSend_decorate(objj_supress_exceptions_decorator)
// Uncomment to enable runtime type checking:
//objj_msgSend_decorate(objj_typecheck_decorator);
// Uncomment (along with both above) to print backtraces on type check errors:
//objj_typecheck_prints_backtrace = true;
// Uncomment to disable the default logger (CPLogConsole if window.console exists, CPLogPopup otherwise):
//CPLogUnregister(CPLogDefault);
// Uncomment to enable a specific logger:
//CPLogRegister(CPLogConsole);
//CPLogRegister(CPLogPopup);
// Tag view DOM elements with a "data-cappuccino-view" attribute that contains
// the class name of the view that created them. Comment this or set to false to disable.
appkit_tag_dom_elements = true;
</script>
<style type="text/css">
html, body, h1, p {
margin: 0;
padding: 0;
}
/* We need a body wrapper because Cappuccino is unhappy if we change the body element */
#cappuccino-body {
/* Position it absolutely so it will fill the height without content */
position: absolute;
top: 0;
bottom: 0;
width: 100%;
/* Put it at the bottom of the stack so it doesn't interfere with UI */
z-index: -1000;
}
#cappuccino-body .container {
display: table;
margin: 0 auto;
height: 100%;
}
#cappuccino-body .content {
display: table-cell;
height: 100%;
vertical-align: top;
}
#loading {
position: relative;
top: 35%;
}
#loading-text {
height: 1.5em;
color: #555;
font: normal bold 36px/36px Arial, sans-serif;
}
#progress-indicator {
padding: 0px;
height: 16px;
border: 5px solid #555;
border-radius: 18px;
background-color: white;
}
#progress-bar {
position: relative;
top: -1px;
left: -1px;
display: block;
height: 18px;
/* Compensate for moving the bar left 1px to overlap the indicator border */
border-right: 1px solid #555;
background-color: #555;
}
#noscript {
position: relative;
top: 35%;
padding: 1em 1.5em;
border: 5px solid #555;
border-radius: 16px;
background-color: white;
color: #555;
text-align: center;
font: bold 24px Arial, sans-serif;
}
#noscript a {
color: #98c0ff;
text-decoration: none;
}
</style>
</head>
<body>
<div id="cappuccino-body">
<div class="container">
<div class="content">
<script type="text/javascript">
document.write(loadingHTML);
</script>
</div>
</div>
<noscript style="position:absolute; top:0; left:0; width:100%; height:100%">
<div class="container">
<div class="content">
<div id="noscript">
<p style="font-size:120%; margin-bottom:.75em">JavaScript is required for this site.</p>
<p><a href="http://www.enable-javascript.com" target="_blank">Enable JavaScript</a></p>
</div>
</div>
</div>
</noscript>
</div>
</body>
</html>
+161
View File
@@ -0,0 +1,161 @@
<!DOCTYPE html>
<html lang="en">
<!--
index.html
CrossOriginTest
Created by You on December 5, 2014.
Copyright 2014, Your Company All rights reserved.
-->
<head>
<meta charset="utf-8">
<!--[if lte IE 8]>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1">
<![endif]-->
<!--[if gte IE 9]>
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<![endif]-->
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="apple-touch-icon" href="Resources/icon.png">
<link rel="apple-touch-startup-image" href="Resources/default.png">
<title>CrossOriginTest</title>
<!-- Custom javascript goes here -->
<!-- End custom javascript -->
<script type="text/javascript">
OBJJ_MAIN_FILE = "main.j";
var progressBar = null;
OBJJ_PROGRESS_CALLBACK = function(percent, appSize, path)
{
percent = percent * 100;
if (!progressBar)
progressBar = document.getElementById("progress-bar");
if (progressBar)
progressBar.style.width = Math.min(percent, 100) + "%";
}
var loadingHTML =
'<div id="loading">' +
' <div id="loading-text">Loading...</div>' +
' <div id="progress-indicator">' +
' <span id="progress-bar" style="width:0%"></span>' +
' </div>' +
'</div>';
</script>
<script type="text/javascript" src="Frameworks/Objective-J/Objective-J.js" charset="UTF-8"></script>
<style type="text/css">
html, body, h1, p {
margin: 0;
padding: 0;
}
/* We need a body wrapper because Cappuccino is unhappy if we change the body element */
#cappuccino-body {
/* Position it absolutely so it will fill the height without content */
position: absolute;
top: 0;
bottom: 0;
width: 100%;
/* Put it at the bottom of the stack so it doesn't interfere with UI */
z-index: -1000;
}
#cappuccino-body .container {
display: table;
margin: 0 auto;
height: 100%;
}
#cappuccino-body .content {
display: table-cell;
height: 100%;
vertical-align: top;
}
#loading {
position: relative;
top: 35%;
}
#loading-text {
height: 1.5em;
color: #555;
font: normal bold 36px/36px Arial, sans-serif;
}
#progress-indicator {
padding: 0px;
height: 16px;
border: 5px solid #555;
border-radius: 18px;
background-color: white;
}
#progress-bar {
position: relative;
top: -1px;
left: -1px;
display: block;
height: 18px;
/* Compensate for moving the bar left 1px to overlap the indicator border */
border-right: 1px solid #555;
background-color: #555;
}
#noscript {
position: relative;
top: 35%;
padding: 1em 1.5em;
border: 5px solid #555;
border-radius: 16px;
background-color: white;
color: #555;
text-align: center;
font: bold 24px Arial, sans-serif;
}
#noscript a {
color: #98c0ff;
text-decoration: none;
}
</style>
</head>
<body>
<div id="cappuccino-body">
<div class="container">
<div class="content">
<script type="text/javascript">
document.write(loadingHTML);
</script>
</div>
</div>
<noscript style="position:absolute; top:0; left:0; width:100%; height:100%">
<div class="container">
<div class="content">
<div id="noscript">
<p style="font-size:120%; margin-bottom:.75em">JavaScript is required for this site.</p>
<p><a href="http://www.enable-javascript.com" target="_blank">Enable JavaScript</a></p>
</div>
</div>
</div>
</noscript>
</div>
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
/*
* AppController.j
* CrossOriginTest
*
* Created by You on December 9, 2014.
* Copyright 2014, Your Company All rights reserved.
*/
@import <Foundation/Foundation.j>
@import <AppKit/AppKit.j>
@import "AppController.j"
function main(args, namedArgs)
{
CPApplicationMain(args, namedArgs);
}
+1
View File
@@ -0,0 +1 @@
{"response": "ok"}