Skip to content

Commit 79cd718

Browse files
authored
preserve trailing slash in uri path (#107)
1 parent 6efe214 commit 79cd718

File tree

3 files changed

+74
-4
lines changed

3 files changed

+74
-4
lines changed

Sources/AsyncHTTPClient/HTTPHandler.swift

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,14 +385,48 @@ extension HTTPClientResponseDelegate {
385385
public func didReceiveError(task: HTTPClient.Task<Response>, _: Error) {}
386386
}
387387

388-
internal extension URL {
388+
extension URL {
389+
var percentEncodedPath: String {
390+
if self.path.isEmpty {
391+
return "/"
392+
}
393+
return self.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? self.path
394+
}
395+
396+
var pathHasTrailingSlash: Bool {
397+
if #available(OSX 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) {
398+
return self.hasDirectoryPath
399+
} else {
400+
// Most platforms should use `self.hasDirectoryPath`, but on older darwin platforms
401+
// we have this approximation instead.
402+
let url = self.absoluteString
403+
404+
var pathEndIndex = url.index(before: url.endIndex)
405+
if let queryIndex = url.firstIndex(of: "?") {
406+
pathEndIndex = url.index(before: queryIndex)
407+
} else if let fragmentIndex = url.suffix(from: url.firstIndex(of: "@") ?? url.startIndex).lastIndex(of: "#") {
408+
pathEndIndex = url.index(before: fragmentIndex)
409+
}
410+
411+
return url[pathEndIndex] == "/"
412+
}
413+
}
414+
389415
var uri: String {
390-
let urlEncodedPath = path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? path
391-
return path.isEmpty ? "/" : urlEncodedPath + (query.map { "?" + $0 } ?? "")
416+
var uri = self.percentEncodedPath
417+
if self.pathHasTrailingSlash, uri != "/" {
418+
uri += "/"
419+
}
420+
421+
if let query = self.query {
422+
uri += "?" + query
423+
}
424+
425+
return uri
392426
}
393427

394428
func hasTheSameOrigin(as other: URL) -> Bool {
395-
return host == other.host && scheme == other.scheme && port == other.port
429+
return self.host == other.host && self.scheme == other.scheme && self.port == other.port
396430
}
397431
}
398432

Tests/AsyncHTTPClientTests/HTTPClientInternalTests+XCTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ extension HTTPClientInternalTests {
3030
("testProxyStreaming", testProxyStreaming),
3131
("testProxyStreamingFailure", testProxyStreamingFailure),
3232
("testUploadStreamingBackpressure", testUploadStreamingBackpressure),
33+
("testRequestURITrailingSlash", testRequestURITrailingSlash),
3334
]
3435
}
3536
}

Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,4 +193,39 @@ class HTTPClientInternalTests: XCTestCase {
193193

194194
XCTAssertEqual(delegate.reads, 3)
195195
}
196+
197+
func testRequestURITrailingSlash() throws {
198+
let request1 = try Request(url: "https://someserver.com:8888/some/path?foo=bar#ref")
199+
XCTAssertEqual(request1.url.uri, "/some/path?foo=bar")
200+
201+
let request2 = try Request(url: "https://someserver.com:8888/some/path/?foo=bar#ref")
202+
XCTAssertEqual(request2.url.uri, "/some/path/?foo=bar")
203+
204+
let request3 = try Request(url: "https://someserver.com:8888?foo=bar#ref")
205+
XCTAssertEqual(request3.url.uri, "/?foo=bar")
206+
207+
let request4 = try Request(url: "https://someserver.com:8888/?foo=bar#ref")
208+
XCTAssertEqual(request4.url.uri, "/?foo=bar")
209+
210+
let request5 = try Request(url: "https://someserver.com:8888/some/path")
211+
XCTAssertEqual(request5.url.uri, "/some/path")
212+
213+
let request6 = try Request(url: "https://someserver.com:8888/some/path/")
214+
XCTAssertEqual(request6.url.uri, "/some/path/")
215+
216+
let request7 = try Request(url: "https://someserver.com:8888")
217+
XCTAssertEqual(request7.url.uri, "/")
218+
219+
let request8 = try Request(url: "https://someserver.com:8888/")
220+
XCTAssertEqual(request8.url.uri, "/")
221+
222+
let request9 = try Request(url: "https://someserver.com:8888#ref")
223+
XCTAssertEqual(request9.url.uri, "/")
224+
225+
let request10 = try Request(url: "https://someserver.com:8888/#ref")
226+
XCTAssertEqual(request10.url.uri, "/")
227+
228+
let request11 = try Request(url: "https://someserver.com/some%20path")
229+
XCTAssertEqual(request11.url.uri, "/some%20path")
230+
}
196231
}

0 commit comments

Comments
 (0)