Index: src/OFHTTPClient.m ================================================================== --- src/OFHTTPClient.m +++ src/OFHTTPClient.m @@ -20,17 +20,18 @@ #include #include #import "OFHTTPClient.h" +#import "OFData.h" +#import "OFDictionary.h" #import "OFHTTPRequest.h" #import "OFHTTPResponse.h" +#import "OFNumber.h" #import "OFString.h" -#import "OFURL.h" #import "OFTCPSocket.h" -#import "OFDictionary.h" -#import "OFData.h" +#import "OFURL.h" #import "OFAlreadyConnectedException.h" #import "OFHTTPRequestFailedException.h" #import "OFInvalidEncodingException.h" #import "OFInvalidFormatException.h" @@ -80,11 +81,11 @@ constructRequestString(OFHTTPRequest *request) { void *pool = objc_autoreleasePoolPush(); of_http_request_method_t method = [request method]; OFURL *URL = [request URL]; - OFString *scheme = [URL scheme], *path = [URL path]; + OFString *path = [URL path]; OFString *user = [URL user], *password = [URL password]; OFData *body = [request body]; OFMutableString *requestString; OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers; OFEnumerator OF_GENERIC(OFString *) *keyEnumerator, *objectEnumerator; @@ -108,14 +109,15 @@ headers = [[[request headers] mutableCopy] autorelease]; if (headers == nil) headers = [OFMutableDictionary dictionary]; if ([headers objectForKey: @"Host"] == nil) { - if (([scheme isEqual: @"http"] && [URL port] != 80) || - ([scheme isEqual: @"https"] && [URL port] != 443)) { + OFNumber *port = [URL port]; + + if (port != nil) { OFString *host = [OFString stringWithFormat: - @"%@:%d", [URL host], [URL port]]; + @"%@:%@", [URL host], port]; [headers setObject: host forKey: @"Host"]; } else [headers setObject: [URL host] @@ -707,24 +709,33 @@ - (void)closeAndReconnect { OFURL *URL = [_request URL]; OFTCPSocket *sock; + uint16_t port; + OFNumber *URLPort; [_client close]; if ([[URL scheme] isEqual: @"https"]) { if (of_tls_socket_class == Nil) @throw [OFUnsupportedProtocolException exceptionWithURL: URL]; sock = [[[of_tls_socket_class alloc] init] autorelease]; - } else + port = 443; + } else { sock = [OFTCPSocket socket]; + port = 80; + } + + URLPort = [URL port]; + if (URLPort != nil) + port = [URLPort uInt16Value]; [sock asyncConnectToHost: [URL host] - port: [URL port] + port: port target: self selector: @selector(socketDidConnect:context: exception:) context: nil]; } Index: src/OFHTTPServer.m ================================================================== --- src/OFHTTPServer.m +++ src/OFHTTPServer.m @@ -21,15 +21,16 @@ #import "OFHTTPServer.h" #import "OFData.h" #import "OFDate.h" #import "OFDictionary.h" -#import "OFURL.h" #import "OFHTTPRequest.h" #import "OFHTTPResponse.h" +#import "OFNumber.h" #import "OFTCPSocket.h" #import "OFTimer.h" +#import "OFURL.h" #import "OFAlreadyConnectedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFNotOpenException.h" @@ -652,11 +653,12 @@ } URL = [OFMutableURL URL]; [URL setScheme: @"http"]; [URL setHost: _host]; - [URL setPort: _port]; + if (_port != 80) + [URL setPort: [OFNumber numberWithUInt16: _port]]; if ((pos = [_path rangeOfString: @"?"].location) != OF_NOT_FOUND) { OFString *path, *query; path = [_path substringWithRange: of_range(0, pos)]; Index: src/OFMutableURL.h ================================================================== --- src/OFMutableURL.h +++ src/OFMutableURL.h @@ -25,21 +25,21 @@ */ @interface OFMutableURL: OFURL /*! * The scheme part of the URL. */ -@property (readwrite, copy, nonatomic) OFString *scheme; +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *scheme; /*! * The host part of the URL. */ -@property (readwrite, copy, nonatomic) OFString *host; +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *host; /*! * The port part of the URL. */ -@property (readwrite, nonatomic) uint16_t port; +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFNumber *port; /*! * The user part of the URL. */ @property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *user; @@ -50,11 +50,11 @@ @property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *password; /*! * The path part of the URL. */ -@property (readwrite, copy, nonatomic) OFString *path; +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *path; /*! * The parameters part of the URL. */ @property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) Index: src/OFMutableURL.m ================================================================== --- src/OFMutableURL.m +++ src/OFMutableURL.m @@ -16,10 +16,11 @@ #include "config.h" #import "OFMutableURL.h" #import "OFURL+Private.h" +#import "OFNumber.h" #import "OFString.h" @implementation OFMutableURL @dynamic scheme, host, port, user, password, path, parameters, query, fragment; @@ -45,13 +46,15 @@ OFString *old = _host; _host = [host copy]; [old release]; } -- (void)setPort: (uint16_t)port +- (void)setPort: (OFNumber *)port { - _port = port; + OFNumber *old = _port; + _port = [port copy]; + [old release]; } - (void)setUser: (OFString *)user { OFString *old = _user; Index: src/OFURL.h ================================================================== --- src/OFURL.h +++ src/OFURL.h @@ -17,40 +17,41 @@ #import "OFObject.h" #import "OFSerialization.h" OF_ASSUME_NONNULL_BEGIN +@class OFNumber; @class OFString; /*! * @class OFURL OFURL.h ObjFW/OFURL.h * * @brief A class for parsing URLs and accessing parts of it. */ @interface OFURL: OFObject { - OFString *_scheme, *_host; - uint16_t _port; + OFString *_Nullable _scheme, *_Nullable _host; + OFNumber *_Nullable _port; OFString *_Nullable _user, *_Nullable _password, *_path; OFString *_Nullable _parameters, *_Nullable _query; OFString *_Nullable _fragment; } /*! * The scheme part of the URL. */ -@property (readonly, copy, nonatomic) OFString *scheme; +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *scheme; /*! * The host part of the URL. */ -@property (readonly, copy, nonatomic) OFString *host; +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *host; /*! * The port part of the URL. */ -@property (readonly, nonatomic) uint16_t port; +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFNumber *port; /*! * The user part of the URL. */ @property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *user; @@ -61,11 +62,11 @@ @property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *password; /*! * The path part of the URL. */ -@property (readonly, copy, nonatomic) OFString *path; +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *path; /*! * The parameters part of the URL. */ @property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *parameters; Index: src/OFURL.m ================================================================== --- src/OFURL.m +++ src/OFURL.m @@ -19,12 +19,13 @@ #include #include #import "OFURL.h" #import "OFURL+Private.h" -#import "OFString.h" #import "OFArray.h" +#import "OFNumber.h" +#import "OFString.h" #import "OFXMLElement.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFOutOfMemoryException.h" @@ -87,11 +88,14 @@ exceptionWithRequestedSize: [string UTF8StringLength]]; UTF8String = UTF8String2; - if ((tmp = strstr(UTF8String, "://")) == NULL) + if ((tmp = strchr(UTF8String, ':')) == NULL) + @throw [OFInvalidFormatException exception]; + + if (strncmp(tmp, "://", 3) != 0) @throw [OFInvalidFormatException exception]; for (tmp2 = UTF8String; tmp2 < tmp; tmp2++) *tmp2 = of_ascii_tolower(*tmp2); @@ -99,18 +103,10 @@ initWithUTF8String: UTF8String length: tmp - UTF8String]; UTF8String = tmp + 3; - if ([_scheme isEqual: @"file"]) { - _path = [[OFString alloc] - initWithUTF8String: UTF8String]; - - objc_autoreleasePoolPop(pool); - return self; - } - if ((tmp = strchr(UTF8String, '/')) != NULL) { *tmp = '\0'; tmp++; } @@ -149,25 +145,18 @@ portString = [OFString stringWithUTF8String: tmp2]; if ([portString decimalValue] > 65535) @throw [OFInvalidFormatException exception]; - _port = [portString decimalValue]; + _port = [[OFNumber alloc] initWithUInt16: + (uint16_t)[portString decimalValue]]; objc_autoreleasePoolPop(pool2); - } else { + } else _host = [[OFString alloc] initWithUTF8String: UTF8String]; - if ([_scheme isEqual: @"http"]) - _port = 80; - else if ([_scheme isEqual: @"https"]) - _port = 443; - else if ([_scheme isEqual: @"ftp"]) - _port = 21; - } - if ((UTF8String = tmp) != NULL) { if ((tmp = strchr(UTF8String, '#')) != NULL) { *tmp = '\0'; _fragment = [[OFString alloc] @@ -220,11 +209,11 @@ void *pool = objc_autoreleasePoolPush(); char *tmp; _scheme = [URL->_scheme copy]; _host = [URL->_host copy]; - _port = URL->_port; + _port = [URL->_port copy]; _user = [URL->_user copy]; _password = [URL->_password copy]; if ((UTF8String2 = of_strdup([string UTF8String])) == NULL) @throw [OFOutOfMemoryException @@ -302,10 +291,11 @@ - (void)dealloc { [_scheme release]; [_host release]; + [_port release]; [_user release]; [_password release]; [_path release]; [_parameters release]; [_query release]; @@ -321,31 +311,28 @@ if (![object isKindOfClass: [OFURL class]]) return false; URL = object; - if (![URL->_scheme isEqual: _scheme]) + if (URL->_scheme != _scheme && ![URL->_scheme isEqual: _scheme]) return false; - if (![URL->_host isEqual: _host]) + if (URL->_host != _host && ![URL->_host isEqual: _host]) return false; - if (URL->_port != _port) + if (URL->_port != _port && ![URL->_port isEqual: _port]) return false; if (URL->_user != _user && ![URL->_user isEqual: _user]) return false; - if (URL->_password != _password && - ![URL->_password isEqual: _password]) + if (URL->_password != _password && ![URL->_password isEqual: _password]) return false; - if (![URL->_path isEqual: _path]) + if (URL->_path != _path && ![URL->_path isEqual: _path]) return false; if (URL->_parameters != _parameters && ![URL->_parameters isEqual: _parameters]) return false; - if (URL->_query != _query && - ![URL->_query isEqual: _query]) + if (URL->_query != _query && ![URL->_query isEqual: _query]) return false; - if (URL->_fragment != _fragment && - ![URL->_fragment isEqual: _fragment]) + if (URL->_fragment != _fragment && ![URL->_fragment isEqual: _fragment]) return false; return true; } @@ -355,12 +342,11 @@ OF_HASH_INIT(hash); OF_HASH_ADD_HASH(hash, [_scheme hash]); OF_HASH_ADD_HASH(hash, [_host hash]); - OF_HASH_ADD(hash, (_port & 0xFF00) >> 8); - OF_HASH_ADD(hash, _port & 0xFF); + OF_HASH_ADD_HASH(hash, [_port hash]); OF_HASH_ADD_HASH(hash, [_user hash]); OF_HASH_ADD_HASH(hash, [_password hash]); OF_HASH_ADD_HASH(hash, [_path hash]); OF_HASH_ADD_HASH(hash, [_parameters hash]); OF_HASH_ADD_HASH(hash, [_query hash]); @@ -379,11 +365,11 @@ - (OFString *)host { return _host; } -- (uint16_t)port +- (OFNumber *)port { return _port; } - (OFString *)user @@ -448,30 +434,19 @@ OFMutableString *ret = [OFMutableString string]; void *pool = objc_autoreleasePoolPush(); [ret appendFormat: @"%@://", _scheme]; - if ([_scheme isEqual: @"file"]) { - if (_path != nil) - [ret appendString: _path]; - - objc_autoreleasePoolPop(pool); - return ret; - } - if (_user != nil && _password != nil) [ret appendFormat: @"%@:%@@", _user, _password]; else if (_user != nil) [ret appendFormat: @"%@@", _user]; if (_host != nil) [ret appendString: _host]; - - if (!(([_scheme isEqual: @"http"] && _port == 80) || - ([_scheme isEqual: @"https"] && _port == 443) || - ([_scheme isEqual: @"ftp"] && _port == 21))) - [ret appendFormat: @":%u", _port]; + if (_port != nil) + [ret appendFormat: @":%@", _port]; if (_path != nil) { if (![_path hasPrefix: @"/"]) @throw [OFInvalidFormatException exception]; Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -357,12 +357,11 @@ stringWithContentsOfFile: @"testfile.txt" encoding: OF_STRING_ENCODING_ISO_8859_1]) && [is isEqual: @"testäöü"]) TEST(@"+[stringWithContentsOfURL:encoding]", (is = [stringClass - stringWithContentsOfURL: [OFURL URLWithString: - @"file://testfile.txt"] + stringWithContentsOfURL: [OFURL fileURLWithPath: @"testfile.txt"] encoding: OF_STRING_ENCODING_ISO_8859_1]) && [is isEqual: @"testäöü"]) #endif TEST(@"-[appendUTFString:length:]", Index: tests/OFURLTests.m ================================================================== --- tests/OFURLTests.m +++ tests/OFURLTests.m @@ -15,10 +15,11 @@ */ #include "config.h" #import "OFURL.h" +#import "OFNumber.h" #import "OFString.h" #import "OFAutoreleasePool.h" #import "OFInvalidFormatException.h" @@ -53,11 +54,11 @@ [[[OFURL URLWithString: @"http://foo/?q" relativeToURL: u1] string] isEqual: @"http://foo/?q"]) TEST(@"-[string]", [[u1 string] isEqual: url_str] && - [[u2 string] isEqual: @"http://foo"] && + [[u2 string] isEqual: @"http://foo:80"] && [[u3 string] isEqual: @"http://bar/"] && [[u4 string] isEqual: @"file:///etc/passwd"]) TEST(@"-[scheme]", [[u1 scheme] isEqual: @"ht%3atp"] && [[u4 scheme] isEqual: @"file"]) @@ -64,11 +65,11 @@ TEST(@"-[user]", [[u1 user] isEqual: @"us%3Aer"] && [u4 user] == nil) TEST(@"-[password]", [[u1 password] isEqual: @"p%40w"] && [u4 password] == nil) TEST(@"-[host]", [[u1 host] isEqual: @"ho%3Ast"] && [u4 port] == 0) - TEST(@"-[port]", [u1 port] == 1234) + TEST(@"-[port]", [[u1 port] isEqual: [OFNumber numberWithUInt16: 1234]]) TEST(@"-[path]", [[u1 path] isEqual: @"/pa%3Bth"] && [[u4 path] isEqual: @"/etc/passwd"]) TEST(@"-[parameters]", [[u1 parameters] isEqual: @"pa%3Fram"] && [u4 parameters] == nil) Index: tests/serialization.xml ================================================================== --- tests/serialization.xml +++ tests/serialization.xml @@ -37,16 +37,10 @@ list - MDEyMzQ1Njc4OTo7PEFCQ0RFRkdISklLTE1OT1BRUlNUVVZXWFla - - - data - - Qu"xbar test 1234 40934a456d5cfaad @@ -55,7 +49,13 @@ Hello + + MDEyMzQ1Njc4OTo7PEFCQ0RFRkdISklLTE1OT1BRUlNUVVZXWFla + + + data +