Skip to content

Commit 39e0e5b

Browse files
committed
feat(headers): allow new headers but protect reserved ones
1 parent 6d4410e commit 39e0e5b

File tree

2 files changed

+23
-12
lines changed

2 files changed

+23
-12
lines changed

Sources/TUSKit/TUSClient.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -848,13 +848,13 @@ final class HeaderGenerator {
848848

849849
handler(metaData.id, baseHeaders) { [weak self] headers in
850850
guard let self else {
851-
metaData.updateAppliedCustomHeaders(baseHeaders)
852851
completion(baseHeaders)
853852
return
854853
}
855-
let allowedKeys = Set(baseHeaders.keys)
856854
var sanitized = baseHeaders
857-
for (key, value) in headers where allowedKeys.contains(key) {
855+
for (key, value) in headers {
856+
let lowercasedKey = key.lowercased()
857+
guard !HeaderGenerator.reservedHeaders.contains(lowercasedKey) else { continue }
858858
sanitized[key] = value
859859
}
860860
self.store(headers: sanitized, for: metaData)
@@ -890,6 +890,15 @@ final class HeaderGenerator {
890890
self.latestHeaders[metaData.id] = headers
891891
}
892892
}
893+
894+
private static let reservedHeaders: Set<String> = [
895+
"upload-length",
896+
"upload-offset",
897+
"upload-metadata",
898+
"content-type",
899+
"content-length",
900+
"tus-resumable",
901+
]
893902
}
894903

895904
private extension URL {

Tests/TUSKitTests/TUSClient/TUSClient_HeaderGenerationTests.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,8 @@ final class TUSClient_HeaderGenerationTests: XCTestCase {
349349
XCTAssertTrue(uploadHeaders.contains(customHeaders))
350350
}
351351

352-
/// Ensures new headers introduced by the generator are ignored for protocol safety.
353-
func testGenerateHeadersDoesNotAllowUnknownHeaders() throws {
352+
/// Ensures the generator cannot override headers that TUSClient manages itself.
353+
func testGenerateHeadersCannotOverrideReservedHeaders() throws {
354354
let configuration = URLSessionConfiguration.default
355355
configuration.protocolClasses = [MockURLProtocol.self]
356356
MockURLProtocol.reset()
@@ -368,7 +368,7 @@ final class TUSClient_HeaderGenerationTests: XCTestCase {
368368
generateHeaders: { _, headers, onHeadersGenerated in
369369
var newHeaders = headers
370370
newHeaders["Authorization"] = "Bearer mutated"
371-
newHeaders["X-Injected"] = "should-not-pass"
371+
newHeaders["Upload-Offset"] = "999"
372372
onHeadersGenerated(newHeaders)
373373
}
374374
)
@@ -377,11 +377,13 @@ final class TUSClient_HeaderGenerationTests: XCTestCase {
377377
_ = try client.upload(data: data, customHeaders: customHeaders)
378378
wait(for: [tusDelegate.finishUploadExpectation!], timeout: 5)
379379

380-
XCTAssertTrue(MockURLProtocol.receivedRequests.contains { request in
381-
request.allHTTPHeaderFields?["Authorization"] == "Bearer mutated"
382-
})
383-
XCTAssertFalse(MockURLProtocol.receivedRequests.contains { request in
384-
request.allHTTPHeaderFields?["X-Injected"] != nil
385-
})
380+
let patchRequests = MockURLProtocol.receivedRequests.filter { $0.httpMethod == "PATCH" }
381+
XCTAssertFalse(patchRequests.isEmpty)
382+
guard let patchHeaders = patchRequests.first?.allHTTPHeaderFields else {
383+
XCTFail("Expected PATCH request headers")
384+
return
385+
}
386+
XCTAssertEqual(patchHeaders["Authorization"], "Bearer mutated")
387+
XCTAssertNotEqual(patchHeaders["Upload-Offset"], "999")
386388
}
387389
}

0 commit comments

Comments
 (0)