Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-15284] SwiftFoundation URLProtocol does not have access to httpBody or httpBodyStream for upload tasks #3199

Open
swift-ci opened this issue Oct 6, 2021 · 0 comments

Comments

@swift-ci
Copy link
Contributor

swift-ci commented Oct 6, 2021

Previous ID SR-15284
Radar None
Original Reporter colincornaby (JIRA User)
Type Bug
Environment

swift:latest under Docker

Additional Detail from JIRA
Votes 0
Component/s Foundation
Labels Bug
Assignee None
Priority Medium

md5: 4f4549269b510d966b4ce51d1a0e3c16

Issue Description:

This is a behavior difference from macOS Foundation. This bug is reproducible on Linux, but not on other Apple platforms like iOS or macOS.

URLProtocols typically have access to the httpBody or httpBodyStream. Below shows an example of how to retrieve the body of an upload task.

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif


class TestProtocol: URLProtocol {
    override class func canInit(with task: URLSessionTask) -> Bool {
        return true
    }
    
    override class func canInit(with request: URLRequest) -> Bool {
        return true
    }
    
    override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        return request
    }
    
    override func startLoading() {
        //one of these should be populated with non nil data
        let body = request.httpBody
        let steam = request.httpBodyStream
        client?.urlProtocol(self, didFailWithError: NSError())
    }
    
    override func stopLoading() {
    }
}


let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [TestProtocol.self]
let session = URLSession(configuration: configuration)
var request = URLRequest(url: URL(string: "http://apple.com")!)
request.httpMethod = "POST"
let contents = "Hello Apple"
let task = session.uploadTask(with: request, from: contents.data(using: .utf8)!) { _, _, error in
    
}
task.resume()

On Linux, neither body or stream is ever populated.

This seems to be linked to how Swift Foundation handles body data. On macOS, the request passed to the URLProtocol is altered to include the body data, even if the body data originated from the uploadTask call and wasn't directly populated in the request.

In SwiftFoundation, the body data is shunted into an internal member of URLSessionTask named knownBody. knownBody is then accessible using the internal getBody function. HTTPURLProtocol makes use of the internal getBody function to upload data as part of a POST.

There are several ramifications of the body data not being available:

  • URLProtocol is one of the best ways to mock URLSession for unit testing. These tests will fail on SwiftFoundation platforms. (This is the case I hit.)

  • It's impossible to build a custom URLProtocol outside of SwiftFoundation that works reliably with upload data. I can't find a way that the URLProtocol would have access to the data that is to be uploaded in a public way.

  • There's a behavior difference from Apple platforms.

My proposal would be that the request given to the URLRequest should be populated to match Apple Foundation behavior. Changes could be introduced to classes HTTPURLProtocol to migrate over to a public supported URLRequest version of fetching data, instead of using the internal getBody function.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant