Back out "Reapply Fix escaping in the URL conversion" (#37103)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/37103 Re backout the fix as there are some edge cases not handled properly internally. ## Changelog: [iOS][Fixed] - Revert change in URL escaping Reviewed By: javache, sammy-SC, rshest Differential Revision: D45309232 fbshipit-source-id: d9f473d1f6409beb1069d9af7e649ee5b1b06d6e
This commit is contained in:
Родитель
495506db78
Коммит
5ff19f0925
|
@ -83,13 +83,30 @@ RCT_CUSTOM_CONVERTER(NSData *, NSData, [json dataUsingEncoding:NSUTF8StringEncod
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@try { // NSURL has a history of crashing with bad input, so let's be
|
@try { // NSURL has a history of crashing with bad input, so let's be safe
|
||||||
|
|
||||||
NSURLComponents *urlComponents = [NSURLComponents componentsWithString:path]; //[NSURL URLWithString:path];
|
NSURL *URL = [NSURL URLWithString:path];
|
||||||
if (urlComponents.scheme) {
|
if (URL.scheme) { // Was a well-formed absolute URL
|
||||||
return [self _preprocessURLComponents:urlComponents from:path].URL;
|
return URL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if it has a scheme
|
||||||
|
if ([path rangeOfString:@"://"].location != NSNotFound) {
|
||||||
|
NSMutableCharacterSet *urlAllowedCharacterSet = [NSMutableCharacterSet new];
|
||||||
|
[urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLUserAllowedCharacterSet]];
|
||||||
|
[urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLPasswordAllowedCharacterSet]];
|
||||||
|
[urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLHostAllowedCharacterSet]];
|
||||||
|
[urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLPathAllowedCharacterSet]];
|
||||||
|
[urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||||
|
[urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLFragmentAllowedCharacterSet]];
|
||||||
|
path = [path stringByAddingPercentEncodingWithAllowedCharacters:urlAllowedCharacterSet];
|
||||||
|
URL = [NSURL URLWithString:path];
|
||||||
|
if (URL) {
|
||||||
|
return URL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume that it's a local path
|
||||||
path = path.stringByRemovingPercentEncoding;
|
path = path.stringByRemovingPercentEncoding;
|
||||||
if ([path hasPrefix:@"~"]) {
|
if ([path hasPrefix:@"~"]) {
|
||||||
// Path is inside user directory
|
// Path is inside user directory
|
||||||
|
@ -98,8 +115,7 @@ RCT_CUSTOM_CONVERTER(NSData *, NSData, [json dataUsingEncoding:NSUTF8StringEncod
|
||||||
// Assume it's a resource path
|
// Assume it's a resource path
|
||||||
path = [[NSBundle mainBundle].resourcePath stringByAppendingPathComponent:path];
|
path = [[NSBundle mainBundle].resourcePath stringByAppendingPathComponent:path];
|
||||||
}
|
}
|
||||||
NSURL *URL = [NSURL fileURLWithPath:path];
|
if (!(URL = [NSURL fileURLWithPath:path])) {
|
||||||
if (!URL) {
|
|
||||||
RCTLogConvertError(json, @"a valid URL");
|
RCTLogConvertError(json, @"a valid URL");
|
||||||
}
|
}
|
||||||
return URL;
|
return URL;
|
||||||
|
@ -109,39 +125,6 @@ RCT_CUSTOM_CONVERTER(NSData *, NSData, [json dataUsingEncoding:NSUTF8StringEncod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function preprocess the URLComponents received to make sure that we decode it properly
|
|
||||||
// handling all the use cases.
|
|
||||||
// See the `RCTConvert_NSURLTests` file for a list of use cases that we want to support:
|
|
||||||
// To achieve that, we are currently splitting the url, extracting the fragment, so we can
|
|
||||||
// decode and encode everything but the fragment (which has to be left unmodified)
|
|
||||||
+ (NSURLComponents *)_preprocessURLComponents:(NSURLComponents *)urlComponents from:(NSString *)path
|
|
||||||
{
|
|
||||||
// https://developer.apple.com/documentation/foundation/nsurlcomponents
|
|
||||||
// "[NSURLComponents's] behavior differs subtly from the NSURL class, which conforms to older RFCs"
|
|
||||||
// Specifically, NSURL rejects some URLs that NSURLComponents will handle
|
|
||||||
// gracefully.
|
|
||||||
NSRange fragmentRange = urlComponents.rangeOfFragment;
|
|
||||||
|
|
||||||
if (fragmentRange.length == 0) {
|
|
||||||
// No fragment, pre-remove all escaped characters so we can encode them once if they are present.
|
|
||||||
NSError *error = nil;
|
|
||||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"%[0-90-9]" options:0 error:&error];
|
|
||||||
NSTextCheckingResult *match = [regex firstMatchInString:path options:0 range:NSMakeRange(0, path.length)];
|
|
||||||
if (match) {
|
|
||||||
return [NSURLComponents componentsWithString:path.stringByRemovingPercentEncoding];
|
|
||||||
}
|
|
||||||
return [NSURLComponents componentsWithString:path];
|
|
||||||
}
|
|
||||||
// Pre-remove all escaped characters (excluding the fragment) to handle partially encoded strings
|
|
||||||
NSString *baseUrlString = [path substringToIndex:fragmentRange.location].stringByRemovingPercentEncoding;
|
|
||||||
// Fragment must be kept as they are passed. We don't have to escape them
|
|
||||||
NSString *unmodifiedFragment = [path substringFromIndex:fragmentRange.location];
|
|
||||||
|
|
||||||
// Recreate the url by using a decoded base and an unmodified fragment.
|
|
||||||
NSString *preprocessedURL = [NSString stringWithFormat:@"%@%@", baseUrlString, unmodifiedFragment];
|
|
||||||
return [NSURLComponents componentsWithString:preprocessedURL];
|
|
||||||
}
|
|
||||||
|
|
||||||
RCT_ENUM_CONVERTER(
|
RCT_ENUM_CONVERTER(
|
||||||
NSURLRequestCachePolicy,
|
NSURLRequestCachePolicy,
|
||||||
(@{
|
(@{
|
||||||
|
|
|
@ -77,44 +77,4 @@ TEST_URL(
|
||||||
XCTAssertEqualObjects([testURL absoluteString], [expectedURL absoluteString]);
|
XCTAssertEqualObjects([testURL absoluteString], [expectedURL absoluteString]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escaping edge cases
|
|
||||||
TEST_URL(
|
|
||||||
urlWithMultipleHashes,
|
|
||||||
@"https://example.com/#/abc/#test:example.com",
|
|
||||||
@"https://example.com/#/abc/%23test:example.com")
|
|
||||||
TEST_URL(urlWithEqualsInQuery, @"https://example.com/abc.def?ghi=1234", @"https://example.com/abc.def?ghi=1234")
|
|
||||||
TEST_URL(
|
|
||||||
urlWithEscapedCharacterInFragment,
|
|
||||||
@"https://example.com/abc/def.ghi#jkl-mno%27p-qrs",
|
|
||||||
@"https://example.com/abc/def.ghi#jkl-mno%27p-qrs")
|
|
||||||
TEST_URL(
|
|
||||||
urlWithLongQuery,
|
|
||||||
@"https://example.com/abc?q=def+ghi+jkl&mno=p-q-r-s&tuv=wxy&z_=abc&abc=5",
|
|
||||||
@"https://example.com/abc?q=def+ghi+jkl&mno=p-q-r-s&tuv=wxy&z_=abc&abc=5")
|
|
||||||
TEST_URL(
|
|
||||||
urlWithEscapedCharacterInPathFragment,
|
|
||||||
@"https://example.com/#/abc/%23def%3Aghi.org",
|
|
||||||
@"https://example.com/#/abc/%23def%3Aghi.org")
|
|
||||||
TEST_URL(
|
|
||||||
urlWithEscapedCharacterInQuery,
|
|
||||||
@"https://site.com/script?foo=bar#this_ref",
|
|
||||||
@"https://site.com/script?foo=bar#this_ref")
|
|
||||||
TEST_URL(
|
|
||||||
urlWithUnescapedJson,
|
|
||||||
@"https://example.com/?{\"key\":\"value\"}",
|
|
||||||
@"https://example.com/?%7B%22key%22:%22value%22%7D")
|
|
||||||
TEST_URL(
|
|
||||||
urlWithPartiallyEscapedData,
|
|
||||||
@"https://example.com/?{%22key%22:%22value%22}",
|
|
||||||
@"https://example.com/?%7B%22key%22:%22value%22%7D")
|
|
||||||
TEST_URL(urlWithPercent, @"https://example.com/?width=22%", @"https://example.com/?width=22%25")
|
|
||||||
// NOTE: This is illegal per RFC 3986, but earlier URL specs allowed it
|
|
||||||
TEST_URL(urlWithSquareBracketInPath, @"http://www.foo.com/file[.html", @"http://www.foo.com/file%5B.html")
|
|
||||||
|
|
||||||
TEST_URL(baseDeepLink, @"myapp://launch", @"myapp://launch")
|
|
||||||
TEST_URL(
|
|
||||||
deepLinkWithParams,
|
|
||||||
@"myapp://screen_route?withId=123&prodId=456&isSelected=true",
|
|
||||||
@"myapp://screen_route?withId=123&prodId=456&isSelected=true")
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Загрузка…
Ссылка в новой задаче