Index: src/OFHTTPClient.m
==================================================================
--- src/OFHTTPClient.m
+++ src/OFHTTPClient.m
@@ -316,11 +316,10 @@
{
void *pool = objc_autoreleasePoolPush();
OFURL *URL = [request URL];
OFString *scheme = [URL scheme];
of_http_request_method_t method = [request method];
- OFString *path;
OFMutableString *requestString;
OFString *user, *password;
OFMutableDictionary OF_GENERIC(OFString*, OFString*) *headers;
OFDataArray *body = [request body];
OFTCPSocket *socket;
@@ -373,22 +372,21 @@
/*
* As a work around for a bug with split packets in lighttpd when using
* HTTPS, we construct the complete request in a buffer string and then
* send it all at once.
*/
- path = [[URL path] stringByURLEncodingWithIgnoredCharacters: "/"];
if ([URL query] != nil)
requestString = [OFMutableString stringWithFormat:
- @"%s /%@?%@ HTTP/%@\r\n",
- of_http_request_method_to_string(method), path,
+ @"%s %@?%@ HTTP/%@\r\n",
+ of_http_request_method_to_string(method), [URL path],
[[URL query] stringByURLEncoding],
[request protocolVersionString]];
else
requestString = [OFMutableString stringWithFormat:
- @"%s /%@ HTTP/%@\r\n",
- of_http_request_method_to_string(method), path,
+ @"%s %@ HTTP/%@\r\n",
+ of_http_request_method_to_string(method), [URL path],
[request protocolVersionString]];
headers = [[[request headers] mutableCopy] autorelease];
if (headers == nil)
headers = [OFMutableDictionary dictionary];
Index: src/OFString+URLEncoding.h
==================================================================
--- src/OFString+URLEncoding.h
+++ src/OFString+URLEncoding.h
@@ -36,15 +36,15 @@
/*!
* @brief Encodes a string for use in a URL, but does not escape the specified
* ignored characters.
*
- * @param ignored A C string of characters that should not be escaped
+ * @param allowed A C string of characters that should not be escaped
*
* @return A new autoreleased string
*/
-- (OFString*)stringByURLEncodingWithIgnoredCharacters: (const char*)ignored;
+- (OFString*)stringByURLEncodingWithAllowedCharacters: (const char*)allowed;
/*!
* @brief Decodes a string used in a URL.
*
* @return A new autoreleased string
Index: src/OFString+URLEncoding.m
==================================================================
--- src/OFString+URLEncoding.m
+++ src/OFString+URLEncoding.m
@@ -29,14 +29,14 @@
int _OFString_URLEncoding_reference;
@implementation OFString (URLEncoding)
- (OFString*)stringByURLEncoding
{
- return [self stringByURLEncodingWithIgnoredCharacters: ""];
+ return [self stringByURLEncodingWithAllowedCharacters: "$-_.!*()"];
}
-- (OFString*)stringByURLEncodingWithIgnoredCharacters: (const char*)ignored
+- (OFString*)stringByURLEncodingWithAllowedCharacters: (const char*)allowed
{
void *pool = objc_autoreleasePoolPush();
const char *string = [self UTF8String];
char *retCString;
size_t i;
@@ -57,13 +57,11 @@
/*
* '+' is also listed in RFC 1738, however, '+' is sometimes
* interpreted as space in HTTP. Therefore always escape it to
* make sure it's always interpreted correctly.
*/
- if (!(c & 0x80) && (isalnum(c) || c == '$' || c == '-' ||
- c == '_' || c == '.' || c == '!' || c == '*' || c == '(' ||
- c == ')' || c == ',' || strchr(ignored, c) != NULL))
+ if (!(c & 0x80) && (isalnum(c) || strchr(allowed, c) != NULL))
retCString[i++] = c;
else {
unsigned char high, low;
high = c >> 4;
Index: src/OFURL.m
==================================================================
--- src/OFURL.m
+++ src/OFURL.m
@@ -85,19 +85,19 @@
@throw [OFInvalidFormatException exception];
for (tmp2 = UTF8String; tmp2 < tmp; tmp2++)
*tmp2 = tolower((unsigned char)*tmp2);
- _scheme = [[[OFString stringWithUTF8String: UTF8String
- length: tmp - UTF8String]
- stringByURLDecoding] copy];
+ _scheme = [[OFString alloc]
+ initWithUTF8String: UTF8String
+ length: tmp - UTF8String];
UTF8String = tmp + 3;
if ([_scheme isEqual: @"file"]) {
- _path = [[[OFString stringWithUTF8String:
- UTF8String] stringByURLDecoding] copy];
+ _path = [[OFString alloc]
+ initWithUTF8String: UTF8String];
objc_autoreleasePoolPop(pool);
return self;
}
@@ -114,17 +114,17 @@
if ((tmp3 = strchr(UTF8String, ':')) != NULL) {
*tmp3 = '\0';
tmp3++;
- _user = [[[OFString stringWithUTF8String:
- UTF8String] stringByURLDecoding] copy];
- _password = [[[OFString stringWithUTF8String:
- tmp3] stringByURLDecoding] copy];
+ _user = [[OFString alloc]
+ initWithUTF8String: UTF8String];
+ _password = [[OFString alloc]
+ initWithUTF8String: tmp3];
} else
- _user = [[[OFString stringWithUTF8String:
- UTF8String] stringByURLDecoding] copy];
+ _user = [[OFString alloc]
+ initWithUTF8String: UTF8String];
UTF8String = tmp2;
}
if ((tmp2 = strchr(UTF8String, ':')) != NULL) {
@@ -132,12 +132,12 @@
OFString *portString;
*tmp2 = '\0';
tmp2++;
- _host = [[[OFString stringWithUTF8String:
- UTF8String] stringByURLDecoding] copy];
+ _host = [[OFString alloc]
+ initWithUTF8String: UTF8String];
pool = objc_autoreleasePoolPush();
portString = [OFString stringWithUTF8String: tmp2];
if ([portString decimalValue] > 65535)
@@ -145,12 +145,12 @@
_port = [portString decimalValue];
objc_autoreleasePoolPop(pool);
} else {
- _host = [[[OFString stringWithUTF8String:
- UTF8String] stringByURLDecoding] copy];
+ _host = [[OFString alloc]
+ initWithUTF8String: UTF8String];
if ([_scheme isEqual: @"http"])
_port = 80;
else if ([_scheme isEqual: @"https"])
_port = 443;
@@ -160,30 +160,33 @@
if ((UTF8String = tmp) != NULL) {
if ((tmp = strchr(UTF8String, '#')) != NULL) {
*tmp = '\0';
- _fragment = [[[OFString stringWithUTF8String:
- tmp + 1] stringByURLDecoding] copy];
+ _fragment = [[OFString alloc]
+ initWithUTF8String: tmp + 1];
}
if ((tmp = strchr(UTF8String, '?')) != NULL) {
*tmp = '\0';
- _query = [[[OFString stringWithUTF8String:
- tmp + 1] stringByURLDecoding] copy];
+ _query = [[OFString alloc]
+ initWithUTF8String: tmp + 1];
}
if ((tmp = strchr(UTF8String, ';')) != NULL) {
*tmp = '\0';
- _parameters = [[[OFString stringWithUTF8String:
- tmp + 1] stringByURLDecoding] copy];
+ _parameters = [[OFString alloc]
+ initWithUTF8String: tmp + 1];
}
- _path = [[[OFString stringWithUTF8String:
- UTF8String] stringByURLDecoding] copy];
+ UTF8String--;
+ *UTF8String = '/';
+
+ _path = [[OFString alloc]
+ initWithUTF8String: UTF8String];
}
objc_autoreleasePoolPop(pool);
} @catch (id e) {
[self release];
@@ -222,34 +225,33 @@
UTF8String = UTF8String2;
if ((tmp = strchr(UTF8String, '#')) != NULL) {
*tmp = '\0';
- _fragment = [[[OFString stringWithUTF8String:
- tmp + 1] stringByURLDecoding] copy];
+ _fragment = [[OFString alloc]
+ initWithUTF8String: tmp + 1];
}
if ((tmp = strchr(UTF8String, '?')) != NULL) {
*tmp = '\0';
- _query = [[[OFString stringWithUTF8String:
- tmp + 1] stringByURLDecoding] copy];
+ _query = [[OFString alloc]
+ initWithUTF8String: tmp + 1];
}
if ((tmp = strchr(UTF8String, ';')) != NULL) {
*tmp = '\0';
- _parameters = [[[OFString stringWithUTF8String:
- tmp + 1] stringByURLDecoding] copy];
+ _parameters = [[OFString alloc]
+ initWithUTF8String: tmp + 1];
}
if (*UTF8String == '/')
- _path = [[[OFString stringWithUTF8String:
- UTF8String + 1] stringByURLDecoding] copy];
+ _path = [[OFString alloc]
+ initWithUTF8String: UTF8String];
else {
OFString *path, *s;
- path = [[[OFString stringWithUTF8String:
- UTF8String] stringByURLDecoding] copy];
+ path = [OFString stringWithUTF8String: UTF8String];
if ([URL->_path hasSuffix: @"/"])
s = [URL->_path stringByAppendingString: path];
else
s = [OFString stringWithFormat: @"%@/../%@",
@@ -386,48 +388,48 @@
- (OFString*)string
{
OFMutableString *ret = [OFMutableString string];
void *pool = objc_autoreleasePoolPush();
- [ret appendFormat: @"%@://", [_scheme stringByURLEncoding]];
+ [ret appendFormat: @"%@://", _scheme];
if ([_scheme isEqual: @"file"]) {
if (_path != nil)
- [ret appendString: [_path
- stringByURLEncodingWithIgnoredCharacters: "/"]];
+ [ret appendString: _path];
objc_autoreleasePoolPop(pool);
return ret;
}
if (_user != nil && _password != nil)
- [ret appendFormat: @"%@:%@@",
- [_user stringByURLEncoding],
- [_password stringByURLEncoding]];
+ [ret appendFormat: @"%@:%@@", _user, _password];
else if (_user != nil)
- [ret appendFormat: @"%@@", [_user stringByURLEncoding]];
+ [ret appendFormat: @"%@@", _user];
if (_host != nil)
- [ret appendString: [_host stringByURLEncoding]];
+ [ret appendString: _host];
if (!(([_scheme isEqual: @"http"] && _port == 80) ||
([_scheme isEqual: @"https"] && _port == 443) ||
([_scheme isEqual: @"ftp"] && _port == 21)))
[ret appendFormat: @":%u", _port];
- if (_path != nil)
- [ret appendFormat: @"/%@",
- [_path stringByURLEncodingWithIgnoredCharacters: "/"]];
+ if (_path != nil) {
+ if (![_path hasPrefix: @"/"])
+ @throw [OFInvalidFormatException exception];
+
+ [ret appendString: _path];
+ }
if (_parameters != nil)
- [ret appendFormat: @";%@", [_parameters stringByURLEncoding]];
+ [ret appendFormat: @";%@", _parameters];
if (_query != nil)
- [ret appendFormat: @"?%@", [_query stringByURLEncoding]];
+ [ret appendFormat: @"?%@", _query];
if (_fragment != nil)
- [ret appendFormat: @"#%@", [_fragment stringByURLEncoding]];
+ [ret appendFormat: @"#%@", _fragment];
objc_autoreleasePoolPop(pool);
[ret makeImmutable];
Index: tests/OFURLTests.m
==================================================================
--- tests/OFURLTests.m
+++ tests/OFURLTests.m
@@ -23,11 +23,11 @@
#import "OFInvalidFormatException.h"
#import "TestsAppDelegate.h"
static OFString *module = @"OFURL";
-static OFString *url_str = @"ht%3Atp://us%3Aer:p%40w@ho%3Ast:1234/"
+static OFString *url_str = @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/"
@"pa%3Bth;pa%3Fram?que%23ry#frag%23ment";
@implementation TestsAppDelegate (OFURLTests)
- (void)URLTests
{
@@ -41,11 +41,11 @@
R(u4 = [OFURL URLWithString: @"file:///etc/passwd"]))
TEST(@"+[URLWithString:relativeToURL:]",
[[[OFURL URLWithString: @"/foo"
relativeToURL: u1] string] isEqual:
- @"ht%3Atp://us%3Aer:p%40w@ho%3Ast:1234/foo"] &&
+ @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/foo"] &&
[[[OFURL URLWithString: @"foo/bar?q"
relativeToURL: [OFURL URLWithString: @"http://h/qux/quux"]]
string] isEqual: @"http://h/qux/foo/bar?q"] &&
[[[OFURL URLWithString: @"foo/bar"
relativeToURL: [OFURL URLWithString: @"http://h/qux/?x"]]
@@ -58,24 +58,26 @@
[[u2 string] isEqual: @"http://foo"] &&
[[u3 string] isEqual: @"http://bar/"] &&
[[u4 string] isEqual: @"file:///etc/passwd"])
TEST(@"-[scheme]",
- [[u1 scheme] isEqual: @"ht:tp"] && [[u4 scheme] isEqual: @"file"])
- TEST(@"-[user]", [[u1 user] isEqual: @"us:er"] && [u4 user] == nil)
+ [[u1 scheme] isEqual: @"ht%3atp"] && [[u4 scheme] isEqual: @"file"])
+
+ TEST(@"-[user]", [[u1 user] isEqual: @"us%3Aer"] && [u4 user] == nil)
TEST(@"-[password]",
- [[u1 password] isEqual: @"p@w"] && [u4 password] == nil)
- TEST(@"-[host]", [[u1 host] isEqual: @"ho:st"] && [u4 port] == 0)
+ [[u1 password] isEqual: @"p%40w"] && [u4 password] == nil)
+ TEST(@"-[host]", [[u1 host] isEqual: @"ho%3Ast"] && [u4 port] == 0)
TEST(@"-[port]", [u1 port] == 1234)
TEST(@"-[path]",
- [[u1 path] isEqual: @"pa;th"] &&
+ [[u1 path] isEqual: @"/pa%3Bth"] &&
[[u4 path] isEqual: @"/etc/passwd"])
TEST(@"-[parameters]",
- [[u1 parameters] isEqual: @"pa?ram"] && [u4 parameters] == nil)
- TEST(@"-[query]", [[u1 query] isEqual: @"que#ry"] && [u4 query] == nil)
+ [[u1 parameters] isEqual: @"pa%3Fram"] && [u4 parameters] == nil)
+ TEST(@"-[query]",
+ [[u1 query] isEqual: @"que%23ry"] && [u4 query] == nil)
TEST(@"-[fragment]",
- [[u1 fragment] isEqual: @"frag#ment"] && [u4 fragment] == nil)
+ [[u1 fragment] isEqual: @"frag%23ment"] && [u4 fragment] == nil)
TEST(@"-[copy]", R(u4 = [[u1 copy] autorelease]))
TEST(@"-[isEqual:]", [u1 isEqual: u4] && ![u2 isEqual: u3] &&
[[OFURL URLWithString: @"HTTP://bar/"] isEqual: u3])
Index: tests/serialization.xml
==================================================================
--- tests/serialization.xml
+++ tests/serialization.xml
@@ -6,29 +6,10 @@
- MDEyMzQ1Njc4OTo7PEFCQ0RFRkdISklLTE1OT1BRUlNUVVZXWFla
-
-
-
-
- Qu"xbar
-test
- 1234
- 40934a456d5cfaad
- asd
- 40934a456d5cfaad
-
-
-
-
Hello
Wo
ld!
How are you?
https://webkeks.org/
@@ -55,7 +36,26 @@
+
+ MDEyMzQ1Njc4OTo7PEFCQ0RFRkdISklLTE1OT1BRUlNUVVZXWFla
+
+
+
+
+ Qu"xbar
+test
+ 1234
+ 40934a456d5cfaad
+ asd
+ 40934a456d5cfaad
+
+
+