@@ -92,7 +92,7 @@ public struct MongoConnectionString: Codable, LosslessStringConvertible {
9292 return port
9393 }
9494
95- private enum HostType : String {
95+ internal enum HostType : String {
9696 case ipv4
9797 case ipLiteral = " ip_literal "
9898 case hostname
@@ -105,7 +105,7 @@ public struct MongoConnectionString: Codable, LosslessStringConvertible {
105105 /// The port number.
106106 public let port : UInt16 ?
107107
108- private let type : HostType
108+ internal let type : HostType
109109
110110 /// Initializes a ServerAddress, using the default localhost:27017 if a host/port is not provided.
111111 internal init ( _ hostAndPort: String = " localhost:27017 " ) throws {
@@ -114,7 +114,6 @@ public struct MongoConnectionString: Codable, LosslessStringConvertible {
114114 }
115115
116116 // Check if host is an IPv6 literal.
117- // TODO: SWIFT-1407: support IPv4 address parsing.
118117 if hostAndPort. first == " [ " {
119118 let ipLiteralRegex = try NSRegularExpression ( pattern: #"^\[(.*)\](?::([0-9]+))?$"# )
120119 guard
@@ -135,18 +134,29 @@ public struct MongoConnectionString: Codable, LosslessStringConvertible {
135134 self . type = . ipLiteral
136135 } else {
137136 let parts = hostAndPort. components ( separatedBy: " : " )
138- self . host = String ( parts [ 0 ] )
139137 guard parts. count <= 2 else {
140138 throw MongoError . InvalidArgumentError (
141139 message: " expected only a single port delimiter ':' in \( hostAndPort) "
142140 )
143141 }
142+
143+ let host = parts [ 0 ]
144+ if host. hasSuffix ( " .sock " ) {
145+ self . host = try host. getPercentDecoded ( forKey: " UNIX domain socket " )
146+ self . type = . unixDomainSocket
147+ } else if host. isIPv4 ( ) {
148+ self . host = host
149+ self . type = . ipv4
150+ } else {
151+ self . host = try host. getPercentDecoded ( forKey: " hostname " )
152+ self . type = . hostname
153+ }
154+
144155 if parts. count > 1 {
145156 self . port = try HostIdentifier . parsePort ( from: parts [ 1 ] )
146157 } else {
147158 self . port = nil
148159 }
149- self . type = . hostname
150160 }
151161 }
152162
@@ -420,7 +430,6 @@ public struct MongoConnectionString: Codable, LosslessStringConvertible {
420430 throw MongoError . InvalidArgumentError ( message: " Invalid connection string " )
421431 }
422432 let identifiersAndOptions = schemeAndRest [ 1 ] . components ( separatedBy: " / " )
423- // TODO: SWIFT-1174: handle unescaped slashes in unix domain sockets.
424433 guard identifiersAndOptions. count <= 2 else {
425434 throw MongoError . InvalidArgumentError (
426435 message: " Connection string contains an unescaped slash "
@@ -484,6 +493,9 @@ public struct MongoConnectionString: Codable, LosslessStringConvertible {
484493 self . defaultAuthDB = decoded
485494 // If no other authentication options were provided, we should use the defaultAuthDB as the credential
486495 // source. This will be overwritten later if an authSource is provided.
496+ if self . credential == nil {
497+ self . credential = MongoCredential ( )
498+ }
487499 self . credential? . source = decoded
488500 }
489501
@@ -1033,6 +1045,19 @@ extension StringProtocol {
10331045 return decoded
10341046 }
10351047
1048+ fileprivate func isIPv4( ) -> Bool {
1049+ let numbers = self . components ( separatedBy: " . " )
1050+ guard numbers. count == 4 else {
1051+ return false
1052+ }
1053+ for number in numbers {
1054+ guard let n = Int ( number) , ( 0 ... 255 ) . contains ( n) else {
1055+ return false
1056+ }
1057+ }
1058+ return true
1059+ }
1060+
10361061 fileprivate func getValidatedUserInfo( forKey key: String ) throws -> String {
10371062 for character in MongoConnectionString . forbiddenUserInfoCharacters {
10381063 if self . contains ( character) {
0 commit comments