First, `OWSHttpHeaders(httpHeaders:)` completely ignored its argument. This doesn’t actually seem to have led to any bugs in practice; one time the caller appears to have worked around the bug by adding the headers again, and another time the caller relied on `allHTTPHeaderFields` ignoring unrelated values. Second, `URLRequest` has both `addValue` and `setValue` methods for its headers. The former will construct a comma-separated list if the header is already set, and the latter will replace it if it’s already set. (If the header hasn’t been set, the two are equivalent, which is why call sites weren’t broken even though they used the wrong method.) This was broken only in multi-part uploads, but it was broken for "User-Agent" and "Accept-Language", both of which are non-critical. Third, `URLRequest`’s `allHTTPHeaderFields` doesn’t behave the way you might expect. There’s a unit test which demonstrates some of the weird behaviors, but any fields that aren’t present in the assigned value aren’t touched. It seems as though most code was written as if calling this method would fully replace *all* the HTTP headers. (The `replace(…)` and `removeAllHeaders` methods have been removed because they didn’t do what you’d think, and they weren’t necessary.) Also: * Remove Obj-C support from OWSHttpHeaders * Move & simplify tests for HTTP Retry-After header * Remove unused `asConnectionFailureError` method
121 lines
4.8 KiB
Swift
121 lines
4.8 KiB
Swift
//
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import XCTest
|
|
@testable import SignalServiceKit
|
|
|
|
class OWSHttpHeadersTest: XCTestCase {
|
|
func testFormatAcceptLanguageHeader() throws {
|
|
func chars(_ str: String) -> [String] {
|
|
str.map { String($0) }
|
|
}
|
|
|
|
let testCases: [[String]: String] = [
|
|
[]: "*",
|
|
["invalid!"]: "*",
|
|
["bad1", "bad2", "no_unders", "itstoolong", "en--", "en--us", "en-*stars*", "en-**"]: "*",
|
|
|
|
["en-US"]: "en-US",
|
|
["en-*"]: "en-*",
|
|
["*-US"]: "*-US",
|
|
["*-*"]: "*-*",
|
|
["a-b-c2-d"]: "a-b-c2-d",
|
|
["bad1", "ok", "bad2"]: "ok",
|
|
|
|
// This was an actual string sent from someone's device, so we test that it's ignored.
|
|
["en-US@attribute=isk", "de"]: "de",
|
|
|
|
["a", "b", "c"]: "a, b;q=0.9, c;q=0.8",
|
|
["a", "b", "bad123", "c"]: "a, b;q=0.9, c;q=0.8",
|
|
chars("abcdefghij"): "a, b;q=0.9, c;q=0.8, d;q=0.7, e;q=0.6, f;q=0.5, g;q=0.4, h;q=0.3, i;q=0.2, j;q=0.1",
|
|
chars("abcdefghijklmnopqrst"): "a, b;q=0.9, c;q=0.8, d;q=0.7, e;q=0.6, f;q=0.5, g;q=0.4, h;q=0.3, i;q=0.2, j;q=0.1",
|
|
chars("a!b@c#d$e%f^g&h(i)j_"): "a, b;q=0.9, c;q=0.8, d;q=0.7, e;q=0.6, f;q=0.5, g;q=0.4, h;q=0.3, i;q=0.2, j;q=0.1"
|
|
]
|
|
|
|
for (languages, expected) in testCases {
|
|
let actual = OWSHttpHeaders.formatAcceptLanguageHeader(languages)
|
|
XCTAssertEqual(actual, expected, "Input: \(languages)")
|
|
}
|
|
}
|
|
|
|
func testAcceptLanguageHeaderValue() throws {
|
|
let expected = OWSHttpHeaders.formatAcceptLanguageHeader(Locale.preferredLanguages)
|
|
let actual = OWSHttpHeaders.acceptLanguageHeaderValue
|
|
XCTAssertEqual(actual, expected)
|
|
}
|
|
|
|
func testDebugDescription() {
|
|
let httpHeaders = OWSHttpHeaders()
|
|
httpHeaders.addHeader("Retry-After", value: "Wed, 21 Oct 2015 07:28:01 GMT", overwriteOnConflict: true)
|
|
httpHeaders.addHeader("x-signal-timestamp", value: "1669077270", overwriteOnConflict: true)
|
|
httpHeaders.addHeader("Content-Type", value: "text/plain", overwriteOnConflict: true)
|
|
|
|
XCTAssertEqual(
|
|
"\(httpHeaders)",
|
|
"<OWSHttpHeaders: [Content-Type; Retry-After: Wed, 21 Oct 2015 07:28:01 GMT; x-signal-timestamp: 1669077270]>"
|
|
)
|
|
}
|
|
|
|
/// Test weird behavior with Apple's allHTTPHeaderFields property.
|
|
func testURLRequestAllHTTPHeaderFields() {
|
|
var urlRequest = URLRequest(url: URL(string: "https://signal.org")!)
|
|
urlRequest.allHTTPHeaderFields = ["Retry-After": "1234", "X-Custom": "Value 1"]
|
|
// this does *not* clear any headers
|
|
urlRequest.allHTTPHeaderFields = nil
|
|
// this does *not* clear all headers
|
|
urlRequest.allHTTPHeaderFields = [:]
|
|
// this does *not* clear missing headers
|
|
urlRequest.allHTTPHeaderFields = ["x-custom": "Value 2"]
|
|
XCTAssertEqual(urlRequest.value(forHTTPHeaderField: "X-Custom"), "Value 2")
|
|
XCTAssertEqual(urlRequest.value(forHTTPHeaderField: "Retry-After"), "1234")
|
|
}
|
|
|
|
func testRetryAfter() {
|
|
let now = Date().timeIntervalSince1970
|
|
let testCases: [(String?, TimeInterval?)] = [
|
|
// Reference: date -jf '%a, %d %b %Y %T %Z' <Value> +%s
|
|
("Thu, 01 Jan 1970 00:00:00 GMT", 0),
|
|
("Wed, 21 Oct 2015 07:28:01 GMT", 1445412481),
|
|
|
|
// Reference: date -jf '%Y-%m-%dT%T%z' <Value> +%s
|
|
("1970-01-01T00:00:00+0000", 0),
|
|
("1969-12-31T16:00:00-0800", 0),
|
|
("2015-10-21T07:28:01+0000", 1445412481),
|
|
("2015-10-20T23:28:01-0800", 1445412481),
|
|
|
|
// Relative delays
|
|
("1", now + 1),
|
|
("2", now + 2),
|
|
("1200.0", now + 1200),
|
|
("1200.5", now + 1200.5),
|
|
("86400.000", now + 86400),
|
|
(" 86400.000 ", now + 86400),
|
|
(" \t \t86400.000\t \t ", now + 86400),
|
|
|
|
// Invalid values (these used a default of 60)
|
|
("8/11/1994 01:02:03", now + 60),
|
|
("blahhh", now + 60),
|
|
("one", now + 60),
|
|
("soon", now + 60),
|
|
("later", now + 60),
|
|
|
|
// Absent values (these use nil)
|
|
(nil, nil),
|
|
("", nil),
|
|
(" ", nil),
|
|
("\n", nil)
|
|
]
|
|
|
|
for (headerValue, expectedTimeInterval) in testCases {
|
|
let actualTimeInterval = OWSHttpHeaders.parseRetryAfterHeaderValue(headerValue)?.timeIntervalSince1970
|
|
if let expectedTimeInterval, let actualTimeInterval {
|
|
XCTAssertEqual(actualTimeInterval, expectedTimeInterval, accuracy: 0.3, "\(headerValue ?? "nil")")
|
|
} else {
|
|
XCTAssertEqual(actualTimeInterval, expectedTimeInterval, "\(headerValue ?? "nil")")
|
|
}
|
|
}
|
|
}
|
|
}
|