Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix URL processing code #1180

Merged
merged 5 commits into from Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 38 additions & 42 deletions Apps/UnitTests/Scripts/tests.js
Expand Up @@ -31,62 +31,58 @@ const expect = chai.expect;
describe("XMLHTTPRequest", function () {
this.timeout(0);
it("should have readyState=4 when load ends", async function () {
const xhr = await createRequest("GET", "https://babylonjs.com");
const xhr = await createRequest("GET", "https://httpbin.org/get");
expect(xhr.readyState).to.equal(4);
})
});
it("should have status=200 for a file that exists", async function () {
const xhr = await createRequest("GET", "https://babylonjs.com");
expect(xhr.status).to.equal(200);
})
it("should load unescaped URLs", async function () {
const xhr = await createRequest("GET", "https://github.com/BabylonJS/Assets/raw/master/meshes/στρογγυλεμένος % κύβος.glb");
const xhr = await createRequest("GET", "https://httpbin.org/status/200");
expect(xhr.status).to.equal(200);
})
it("should load partially unescaped URLs", async function () {
const xhr = await createRequest("GET", "https://github.com/BabylonJS/Assets/raw/master/meshes/στρογγυλεμένος%20%%20κύβος.glb");
});
it("should load URLs with escaped unicode characters", async function () {
const xhr = await createRequest("GET", "https://raw.githubusercontent.com/BabylonJS/Assets/master/meshes/%CF%83%CF%84%CF%81%CE%BF%CE%B3%CE%B3%CF%85%CE%BB%CE%B5%CE%BC%CE%AD%CE%BD%CE%BF%CF%82%20%25%20%CE%BA%CF%8D%CE%B2%CE%BF%CF%82.glb");
expect(xhr.status).to.equal(200);
})
it("should load escaped URLs", async function () {
const xhr = await createRequest("GET", "https://github.com/BabylonJS/Assets/raw/master/meshes/%CF%83%CF%84%CF%81%CE%BF%CE%B3%CE%B3%CF%85%CE%BB%CE%B5%CE%BC%CE%AD%CE%BD%CE%BF%CF%82%20%25%20%CE%BA%CF%8D%CE%B2%CE%BF%CF%82.glb");
});
it("should load URLs with unescaped unicode characters", async function () {
const xhr = await createRequest("GET", "https://raw.githubusercontent.com/BabylonJS/Assets/master/meshes/στρογγυλεμένος%20%25%20κύβος.glb");
expect(xhr.status).to.equal(200);
})
it("should load URLs with unescaped %s", async function () {
const xhr = await createRequest("GET", "https://github.com/BabylonJS/Assets/raw/master/meshes/%CF%83%CF%84%CF%81%CE%BF%CE%B3%CE%B3%CF%85%CE%BB%CE%B5%CE%BC%CE%AD%CE%BD%CE%BF%CF%82%20%%20%CE%BA%CF%8D%CE%B2%CE%BF%CF%82.glb");
});
it("should load URLs with unescaped unicode characters and spaces", async function () {
const xhr = await createRequest("GET", "https://raw.githubusercontent.com/BabylonJS/Assets/master/meshes/στρογγυλεμένος %25 κύβος.glb");
expect(xhr.status).to.equal(200);
})
});
it("should have status=404 for a file that does not exist", async function () {
const xhr = await createRequest("GET", "https://babylonjs.com/invalid");
const xhr = await createRequest("GET", "https://httpbin.org/status/404");
expect(xhr.status).to.equal(404);
})
});
it("should throw something when opening //", async function () {
function openDoubleSlash() {
const xhr = new XMLHttpRequest();
xhr.open("GET", "//");
xhr.send();
}
expect(openDoubleSlash).to.throw();
})
});
it("should throw something when opening a url with no scheme", function () {
function openNoProtocol() {
const xhr = new XMLHttpRequest();
xhr.open("GET", "noscheme.glb");
xhr.send();
}
expect(openNoProtocol).to.throw();
})
});
it("should throw something when sending before opening", function () {
function sendWithoutOpening() {
const xhr = new XMLHttpRequest();
xhr.send();
}
expect(sendWithoutOpening).to.throw();
})
});
it("should open a local file", async function () {
const xhr = await createRequest("GET", "app:///Scripts/tests.js");
expect(xhr).to.have.property('readyState', 4);
expect(xhr).to.have.property('status', 200);
expect(xhr).to.have.property('responseText').with.lengthOf.above(0);
})
});
});

describe("RequestFile", function () {
Expand All @@ -99,8 +95,8 @@ describe("RequestFile", function () {
);
}
expect(RequestFile).to.throw();
})
})
});
});

describe("ColorParsing", function () {
expect(_native.Canvas.parseColor("")).to.equal(0);
Expand All @@ -120,70 +116,70 @@ describe("ColorParsing", function () {
_native.Canvas.parseColor("unknownColor");
}
expect(incorrectColor).to.throw();
})
});

it("should throw", function () {
function incorrectColor() {
_native.Canvas.parseColor("#");
}
expect(incorrectColor).to.throw();
})
});

it("should throw", function () {
function incorrectColor() {
_native.Canvas.parseColor("#12345");
}
expect(incorrectColor).to.throw();
})
});

it("should throw", function () {
function incorrectColor() {
_native.Canvas.parseColor("rgb(11)");
}
expect(incorrectColor).to.throw();
})
});

it("should throw", function () {
function incorrectColor() {
_native.Canvas.parseColor("rgb(11,22,33");
}
expect(incorrectColor).to.throw();
})
});

it("should throw", function () {
function incorrectColor() {
_native.Canvas.parseColor("rgb(11,22,33,");
}
expect(incorrectColor).to.throw();
})
});

it("should throw", function () {
function incorrectColor() {
_native.Canvas.parseColor("rgba(11, 22, 33, )");
}
expect(incorrectColor).to.throw();
})
});

it("should throw", function () {
function incorrectColor() {
_native.Canvas.parseColor("rgba(11, 22, 33, 44, 55, 66 )");
}
expect(incorrectColor).to.throw();
})
});

it("should throw", function () {
function incorrectColor() {
_native.Canvas.parseColor("rgb");
}
expect(incorrectColor).to.throw();
})
});
it("should throw", function () {
function incorrectColor() {
_native.Canvas.parseColor("rgba");
}
expect(incorrectColor).to.throw();
})
})
});
});

function createSceneAndWait(callback, done) {
const engine = new BABYLON.NativeEngine();
Expand All @@ -192,7 +188,7 @@ function createSceneAndWait(callback, done) {
callback(engine, scene);
scene.executeWhenReady(() => {
done();
})
});
}

describe("Materials", function() {
Expand All @@ -210,7 +206,7 @@ describe("Materials", function() {
}, done);
}
createEmptyShaderMat()
})
});
/*
TODO: Uncomment tests for materials as we implement more features
it("GradientMaterial should compile", function(done) {
Expand All @@ -220,7 +216,7 @@ describe("Materials", function() {
sphere.material = gradientMaterial;
}, done);
});*/
})
});

describe("PostProcesses", function() {
this.timeout(0);
Expand Down Expand Up @@ -325,7 +321,7 @@ describe("PostProcesses", function() {
new BABYLON.ScreenSpaceReflectionPostProcess("ssr", scene, 1.0, camera);
}, done);
});*/
})
});

describe("setTimeout", function () {
this.timeout(1000);
Expand Down Expand Up @@ -423,7 +419,7 @@ describe("setTimeout", function () {
}, i * 10);
}
});
})
});

describe("clearTimeout", function () {
this.timeout(0);
Expand All @@ -438,7 +434,7 @@ describe("clearTimeout", function () {
setTimeout(() => { done(); }, 0);
clearTimeout(undefined);
});
})
});

mocha.run(failures => {
// Test program will wait for code to be set before exiting
Expand Down
29 changes: 19 additions & 10 deletions Dependencies/UrlLib/Source/UrlRequest_Apple.mm
Expand Up @@ -6,6 +6,17 @@

#import <Foundation/Foundation.h>

namespace
{
auto URLAllowedCharacterSet = []()
{
NSRange range;
range.location = 0x21;
range.length = 0x7e - range.location + 1;
return [NSCharacterSet characterSetWithRange:range];
}();
}

namespace UrlLib
{
class UrlRequest::Impl : public ImplBase
Expand All @@ -14,34 +25,32 @@
void Open(UrlMethod method, const std::string& url)
{
m_method = method;
NSString* urlString = [NSString stringWithUTF8String:url.data()];
NSURL* nsURL{[NSURL URLWithString:urlString]};
if (!nsURL || !nsURL.scheme)
m_url = [NSURL URLWithString:[[NSString stringWithUTF8String:url.data()] stringByAddingPercentEncodingWithAllowedCharacters:URLAllowedCharacterSet]];
if (!m_url || !m_url.scheme)
{
throw std::runtime_error{"URL does not have a valid scheme"};
}
NSString* scheme{nsURL.scheme};
NSString* scheme{m_url.scheme};
if ([scheme isEqual:@"app"])
{
NSString* path{[[NSBundle mainBundle] pathForResource:nsURL.path ofType:nil]};
NSString* path = [[NSBundle mainBundle] pathForResource:[m_url.path substringFromIndex:1] ofType:nil];
if (path == nil)
{
throw std::runtime_error{"No file exists at local path"};
}
nsURL = [NSURL fileURLWithPath:path];
m_url = [NSURL fileURLWithPath:path];
}
m_nsURL = nsURL; // Only store the URL if we didn't throw
}

arcana::task<void, std::exception_ptr> SendAsync()
{
if (m_nsURL == nil)
if (m_url == nil)
{
// Complete the task, but retain the default status code of 0 to indicate a client side error.
return arcana::task_from_result<std::exception_ptr>();
}
NSURLSession* session{[NSURLSession sharedSession]};
NSURLRequest* request{[NSURLRequest requestWithURL:m_nsURL]};
NSURLRequest* request{[NSURLRequest requestWithURL:m_url]};

__block arcana::task_completion_source<void, std::exception_ptr> taskCompletionSource{};

Expand Down Expand Up @@ -112,7 +121,7 @@ void Open(UrlMethod method, const std::string& url)
}

private:
NSURL* m_nsURL{};
NSURL* m_url{};
NSData* m_responseBuffer{};
};
}
Expand Down