ObjFW  Check-in [c517c57c89]

Overview
Comment:OFURL: Implement URL-{en,de}coding of IPv6 hosts
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c517c57c8940d2047b053abca6984336d4ffebce7565645040c11a0a6bb8704b
User & Date: js on 2020-03-01 23:40:28
Other Links: manifest | tags
Context
2020-03-07
18:11
.travis.yml: Add notifications check-in: e489612993 user: js tags: trunk
2020-03-02
20:24
Merge branch 'master' into 1.0 check-in: e3bbb35784 user: js tags: 1.0
2020-03-01
23:40
OFURL: Implement URL-{en,de}coding of IPv6 hosts check-in: c517c57c89 user: js tags: trunk
23:07
OFURL: Add support for IPv6 / square bracket hosts check-in: a99f466e3c user: js tags: trunk
Changes

Modified src/OFMutableURL.m from [f41a2347d6] to [1206224cb0].

66
67
68
69
70
71
72




73

74
75
76
77
78
79
80
81
82
83
84





85
86
87
88
89
90
91
92
}

- (void)setHost: (OFString *)host
{
	void *pool = objc_autoreleasePoolPush();
	OFString *old = _URLEncodedHost;





	_URLEncodedHost = [[host stringByURLEncodingWithAllowedCharacters:

	    [OFCharacterSet URLHostAllowedCharacterSet]] copy];

	[old release];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedHost: (OFString *)URLEncodedHost
{
	OFString *old;






	if (URLEncodedHost != nil)
		of_url_verify_escaped(URLEncodedHost,
		    [OFCharacterSet URLHostAllowedCharacterSet]);

	old = _URLEncodedHost;
	_URLEncodedHost = [URLEncodedHost copy];
	[old release];
}







>
>
>
>
|
>
|










>
>
>
>
>
|







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
}

- (void)setHost: (OFString *)host
{
	void *pool = objc_autoreleasePoolPush();
	OFString *old = _URLEncodedHost;

	if (of_url_is_ipv6_host(host))
		_URLEncodedHost = [[OFString alloc]
		    initWithFormat: @"[%@]", host];
	else
		_URLEncodedHost = [[host
		    stringByURLEncodingWithAllowedCharacters:
		    [OFCharacterSet URLHostAllowedCharacterSet]] copy];

	[old release];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedHost: (OFString *)URLEncodedHost
{
	OFString *old;

	if ([URLEncodedHost hasPrefix: @"["] &&
	    [URLEncodedHost hasSuffix: @"]"]) {
		if (!of_url_is_ipv6_host([URLEncodedHost substringWithRange:
		    of_range(1, URLEncodedHost.length - 2)]))
			@throw [OFInvalidFormatException exception];
	} else if (URLEncodedHost != nil)
		of_url_verify_escaped(URLEncodedHost,
		    [OFCharacterSet URLHostAllowedCharacterSet]);

	old = _URLEncodedHost;
	_URLEncodedHost = [URLEncodedHost copy];
	[old release];
}

Modified src/OFURL.h from [b7146fe684] to [6decb7864f].

338
339
340
341
342
343
344








345
346
347
348
/*!
 * @brief Returns the characters allowed in the fragment part of a URL.
 *
 * @return The characters allowed in the fragment part of a URL.
 */
+ (OFCharacterSet *)URLFragmentAllowedCharacterSet;
@end









OF_ASSUME_NONNULL_END

#import "OFMutableURL.h"







>
>
>
>
>
>
>
>




338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/*!
 * @brief Returns the characters allowed in the fragment part of a URL.
 *
 * @return The characters allowed in the fragment part of a URL.
 */
+ (OFCharacterSet *)URLFragmentAllowedCharacterSet;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern bool of_url_is_ipv6_host(OFString *host);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

#import "OFMutableURL.h"

Modified src/OFURL.m from [c0961c24c8] to [0cca3e3685].

65
66
67
68
69
70
71





















72
73
74
75
76
77
78
	OFCharacterSet *_characterSet;
	bool (*_characterIsMember)(id, SEL, of_unichar_t);
}

- (instancetype)of_initWithCharacterSet: (OFCharacterSet *)characterSet
    OF_METHOD_FAMILY(init);
@end






















@implementation OFURLAllowedCharacterSetBase
- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
	OFCharacterSet *_characterSet;
	bool (*_characterIsMember)(id, SEL, of_unichar_t);
}

- (instancetype)of_initWithCharacterSet: (OFCharacterSet *)characterSet
    OF_METHOD_FAMILY(init);
@end

bool
of_url_is_ipv6_host(OFString *host)
{
	const char *UTF8String = host.UTF8String;
	bool hasColon = false;

	while (*UTF8String != '\0') {
		if (!of_ascii_isdigit(*UTF8String) && *UTF8String != ':' &&
		    (*UTF8String < 'a' || *UTF8String > 'f') &&
		    (*UTF8String < 'A' || *UTF8String > 'F'))
			return false;

		if (*UTF8String == ':')
			hasColon = true;

		UTF8String++;
	}

	return hasColon;
}

@implementation OFURLAllowedCharacterSetBase
- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

827
828
829
830
831
832
833











834
835
836
837
838
839
840
- (OFString *)URLEncodedScheme
{
	return _URLEncodedScheme;
}

- (OFString *)host
{











	return _URLEncodedHost.stringByURLDecoding;
}

- (OFString *)URLEncodedHost
{
	return _URLEncodedHost;
}







>
>
>
>
>
>
>
>
>
>
>







848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
- (OFString *)URLEncodedScheme
{
	return _URLEncodedScheme;
}

- (OFString *)host
{
	if ([_URLEncodedHost hasPrefix: @"["] &&
	    [_URLEncodedHost hasSuffix: @"]"]) {
		OFString *host = [_URLEncodedHost substringWithRange:
		    of_range(1, _URLEncodedHost.length - 2)];

		if (!of_url_is_ipv6_host(host))
			@throw [OFInvalidArgumentException exception];

		return host;
	}

	return _URLEncodedHost.stringByURLDecoding;
}

- (OFString *)URLEncodedHost
{
	return _URLEncodedHost;
}

Modified tests/OFURLTests.m from [b020f4fabb] to [8b6c1d8b68].

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
	TEST(@"-[scheme]",
	    [u1.scheme isEqual: @"ht:tp"] && [u4.scheme isEqual: @"file"])

	TEST(@"-[user]", [u1.user isEqual: @"us:er"] && u4.user == nil)
	TEST(@"-[password]",
	    [u1.password isEqual: @"p@w"] && u4.password == nil)
	TEST(@"-[host]", [u1.host isEqual: @"ho:st"] &&
	    [u6.host isEqual: @"[12:34::56:abcd]"] &&
	    [u7.host isEqual: @"[12:34::56:abcd]"])
	TEST(@"-[port]", [u1.port isEqual: [OFNumber numberWithUInt16: 1234]] &&
	    [u4 port] == nil &&
	    [u7.port isEqual: [OFNumber numberWithUInt16: 234]])
	TEST(@"-[path]",
	    [u1.path isEqual: @"/pa?th"] && [u4.path isEqual: @"/etc/passwd"])
	TEST(@"-[pathComponents]",
	    [u1.pathComponents isEqual:







|
|







150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
	TEST(@"-[scheme]",
	    [u1.scheme isEqual: @"ht:tp"] && [u4.scheme isEqual: @"file"])

	TEST(@"-[user]", [u1.user isEqual: @"us:er"] && u4.user == nil)
	TEST(@"-[password]",
	    [u1.password isEqual: @"p@w"] && u4.password == nil)
	TEST(@"-[host]", [u1.host isEqual: @"ho:st"] &&
	    [u6.host isEqual: @"12:34::56:abcd"] &&
	    [u7.host isEqual: @"12:34::56:abcd"])
	TEST(@"-[port]", [u1.port isEqual: [OFNumber numberWithUInt16: 1234]] &&
	    [u4 port] == nil &&
	    [u7.port isEqual: [OFNumber numberWithUInt16: 234]])
	TEST(@"-[path]",
	    [u1.path isEqual: @"/pa?th"] && [u4.path isEqual: @"/etc/passwd"])
	TEST(@"-[pathComponents]",
	    [u1.pathComponents isEqual:
202
203
204
205
206
207
208
209






210
211
212


213
214
215






216
217
218
219
220
221
222
	    (mu.URLEncodedScheme = @"ht%3Atp") && [mu.scheme isEqual: @"ht:tp"])

	EXPECT_EXCEPTION(
	    @"-[setURLEncodedScheme:] with invalid characters fails",
	    OFInvalidFormatException, mu.URLEncodedScheme = @"~")

	TEST(@"-[setHost:]",
	    (mu.host = @"ho:st") && [mu.URLEncodedHost isEqual: @"ho%3Ast"])







	TEST(@"-[setURLEncodedHost:]",
	    (mu.URLEncodedHost = @"ho%3Ast") && [mu.host isEqual: @"ho:st"])



	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails",
	    OFInvalidFormatException, mu.URLEncodedHost = @"/")







	TEST(@"-[setUser:]",
	    (mu.user = @"us:er") && [mu.URLEncodedUser isEqual: @"us%3Aer"])

	TEST(@"-[setURLEncodedUser:]",
	    (mu.URLEncodedUser = @"us%3Aer") && [mu.user isEqual: @"us:er"])








|
>
>
>
>
>
>


|
>
>

|
|
>
>
>
>
>
>







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
	    (mu.URLEncodedScheme = @"ht%3Atp") && [mu.scheme isEqual: @"ht:tp"])

	EXPECT_EXCEPTION(
	    @"-[setURLEncodedScheme:] with invalid characters fails",
	    OFInvalidFormatException, mu.URLEncodedScheme = @"~")

	TEST(@"-[setHost:]",
	    (mu.host = @"ho:st") && [mu.URLEncodedHost isEqual: @"ho%3Ast"] &&
	    (mu.host = @"12:34:ab") &&
	    [mu.URLEncodedHost isEqual: @"[12:34:ab]"] &&
	    (mu.host = @"12:34:aB") &&
	    [mu.URLEncodedHost isEqual: @"[12:34:aB]"] &&
	    (mu.host = @"12:34:g") &&
	    [mu.URLEncodedHost isEqual: @"12%3A34%3Ag"])

	TEST(@"-[setURLEncodedHost:]",
	    (mu.URLEncodedHost = @"ho%3Ast") && [mu.host isEqual: @"ho:st"] &&
	    (mu.URLEncodedHost = @"[12:34]") && [mu.host isEqual: @"12:34"] &&
	    (mu.URLEncodedHost = @"[12::ab]") && [mu.host isEqual: @"12::ab"])

	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails"
	    " #1", OFInvalidFormatException, mu.URLEncodedHost = @"/")

	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails"
	    " #2", OFInvalidFormatException, mu.URLEncodedHost = @"[12:34")

	EXPECT_EXCEPTION(@"-[setURLEncodedHost:] with invalid characters fails"
	    " #3", OFInvalidFormatException, mu.URLEncodedHost = @"[a::g]")

	TEST(@"-[setUser:]",
	    (mu.user = @"us:er") && [mu.URLEncodedUser isEqual: @"us%3Aer"])

	TEST(@"-[setURLEncodedUser:]",
	    (mu.URLEncodedUser = @"us%3Aer") && [mu.user isEqual: @"us:er"])