Skip to content

Commit e424b2c

Browse files
committed
re-sanitizing for supportsSecureCoding
1 parent 28ac066 commit e424b2c

File tree

2 files changed

+40
-36
lines changed

2 files changed

+40
-36
lines changed

Sources/FoundationNetworking/URLResponse.swift

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ open class URLResponse : NSObject, NSSecureCoding, NSCopying, @unchecked Sendabl
3535
guard let nsurl = aDecoder.decodeObject(of: NSURL.self, forKey: "NS.url") else { return nil }
3636
self.url = nsurl as URL
3737

38-
3938
if let mimetype = aDecoder.decodeObject(of: NSString.self, forKey: "NS.mimeType") {
4039
self.mimeType = mimetype as String
4140
}
@@ -46,12 +45,9 @@ open class URLResponse : NSObject, NSSecureCoding, NSCopying, @unchecked Sendabl
4645
self.textEncodingName = encodedEncodingName as String
4746
}
4847

49-
if let encodedFilename = aDecoder.decodeObject(of: NSString.self, forKey: "NS.suggestedFilename")?.lastPathComponent,
50-
!encodedFilename.isEmpty {
51-
self.suggestedFilename = encodedFilename
52-
} else {
53-
self.suggestedFilename = "Unknown"
54-
}
48+
// re-sanitizing with lastPathComponent because of supportsSecureCoding
49+
let encodedFilename = aDecoder.decodeObject(of: NSString.self, forKey: "NS.suggestedFilename")?.lastPathComponent
50+
self.suggestedFilename = encodedFilename?.isEmpty != false ? "Unknown" : encodedFilename
5551
}
5652

5753
open func encode(with aCoder: NSCoder) {
@@ -180,6 +176,25 @@ open class URLResponse : NSObject, NSSecureCoding, NSCopying, @unchecked Sendabl
180176
/// protocol responses.
181177
open class HTTPURLResponse : URLResponse, @unchecked Sendable {
182178

179+
private static func sanitize(headerFields: [String: String]?) -> [String: String] {
180+
// Canonicalize the header fields by capitalizing the field names, but not X- Headers
181+
// This matches the behaviour of Darwin.
182+
guard let headerFields = headerFields else { return [:] }
183+
var canonicalizedFields: [String: String] = [:]
184+
185+
for (key, value) in headerFields {
186+
if key.isEmpty { continue }
187+
if key.hasPrefix("x-") || key.hasPrefix("X-") {
188+
canonicalizedFields[key] = value
189+
} else if key.caseInsensitiveCompare("WWW-Authenticate") == .orderedSame {
190+
canonicalizedFields["WWW-Authenticate"] = value
191+
} else {
192+
canonicalizedFields[key.capitalized] = value
193+
}
194+
}
195+
return canonicalizedFields
196+
}
197+
183198
/// Initializer for HTTPURLResponse objects.
184199
///
185200
/// - Parameter url: the URL from which the response was generated.
@@ -189,30 +204,13 @@ open class HTTPURLResponse : URLResponse, @unchecked Sendable {
189204
/// - Returns: the instance of the object, or `nil` if an error occurred during initialization.
190205
public init?(url: URL, statusCode: Int, httpVersion: String?, headerFields: [String : String]?) {
191206
self.statusCode = statusCode
192-
193-
self._allHeaderFields = {
194-
// Canonicalize the header fields by capitalizing the field names, but not X- Headers
195-
// This matches the behaviour of Darwin.
196-
guard let headerFields = headerFields else { return [:] }
197-
var canonicalizedFields: [String: String] = [:]
198-
199-
for (key, value) in headerFields {
200-
if key.isEmpty { continue }
201-
if key.hasPrefix("x-") || key.hasPrefix("X-") {
202-
canonicalizedFields[key] = value
203-
} else if key.caseInsensitiveCompare("WWW-Authenticate") == .orderedSame {
204-
canonicalizedFields["WWW-Authenticate"] = value
205-
} else {
206-
canonicalizedFields[key.capitalized] = value
207-
}
208-
}
209-
return canonicalizedFields
210-
}()
211-
207+
208+
self._allHeaderFields = HTTPURLResponse.sanitize(headerFields: headerFields)
209+
212210
super.init(url: url, mimeType: nil, expectedContentLength: 0, textEncodingName: nil)
213-
expectedContentLength = getExpectedContentLength(fromHeaderFields: headerFields) ?? -1
214-
suggestedFilename = getSuggestedFilename(fromHeaderFields: headerFields) ?? "Unknown"
215-
if let type = ContentTypeComponents(headerFields: headerFields) {
211+
expectedContentLength = getExpectedContentLength(fromHeaderFields: _allHeaderFields) ?? -1
212+
suggestedFilename = getSuggestedFilename(fromHeaderFields: _allHeaderFields) ?? "Unknown"
213+
if let type = ContentTypeComponents(headerFields: _allHeaderFields) {
216214
mimeType = type.mimeType.lowercased()
217215
textEncodingName = type.textEncoding?.lowercased()
218216
}
@@ -225,13 +223,18 @@ open class HTTPURLResponse : URLResponse, @unchecked Sendable {
225223

226224
self.statusCode = aDecoder.decodeInteger(forKey: "NS.statusCode")
227225

228-
if aDecoder.containsValue(forKey: "NS.allHeaderFields") {
229-
self._allHeaderFields = aDecoder.decodeObject(of: NSDictionary.self, forKey: "NS.allHeaderFields") as! [String: String]
230-
} else {
231-
self._allHeaderFields = [:]
232-
}
226+
// re-sanitizing dictionary because of supportsSecureCoding
227+
self._allHeaderFields = HTTPURLResponse.sanitize(headerFields: aDecoder.decodeObject(of: NSDictionary.self, forKey: "NS.allHeaderFields") as? [String: String])
233228

234229
super.init(coder: aDecoder)
230+
231+
// re-sanitizing from _allHeaderFields because of supportsSecureCoding
232+
expectedContentLength = getExpectedContentLength(fromHeaderFields: _allHeaderFields) ?? -1
233+
suggestedFilename = getSuggestedFilename(fromHeaderFields: _allHeaderFields) ?? "Unknown"
234+
if let type = ContentTypeComponents(headerFields: _allHeaderFields) {
235+
mimeType = type.mimeType.lowercased()
236+
textEncodingName = type.textEncoding?.lowercased()
237+
}
235238
}
236239

237240
open override func encode(with aCoder: NSCoder) {

Tests/Foundation/TestHTTPURLResponse.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ class TestHTTPURLResponse: XCTestCase {
228228

229229
func test_NSCoding() {
230230
let url = URL(string: "https://apple.com")!
231-
let f = ["Content-Type": "text/HTML; charset=ISO-8859-4"]
231+
let f = ["Content-Type": "text/HTML; charset=ISO-8859-4",
232+
"Content-Disposition": "attachment; filename=fname.ext"]
232233

233234
let responseA = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: f)!
234235
let responseB = NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: responseA)) as! HTTPURLResponse

0 commit comments

Comments
 (0)