URLSessionUploadTask 立即自动取消
URLSessionUploadTask getting automatically cancelled instantly
我遇到了一个奇怪的问题,新创建的 URLSessionUploadTask
会立即被取消。我不确定这是否是当前 Xcode 8.
测试版的错误
我怀疑这可能是一个错误,因为我要 post 运行 的代码正好一次。之后没有对其进行任何更改,然后它就停止工作了。是的,它确实 运行 一次,然后就停止工作了。我将 post 接近尾声的错误。
我将 post 下面的代码,但首先我将总结一下这里的逻辑是如何工作的。
我的测试,或用户公开的 API(用于 Playgrounds 或直接在应用程序上使用的 IE),调用 authorize
方法。此 authorize
方法将依次调用 buildPOSTTask
,这将构造一个有效的 URL 和 return 一个 URLSessionUploadTask
以供 authorize
方法使用.
话虽如此,代码如下:
会话:
internal let urlSession = URLSession(configuration: .default)
创建上传任务函数:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
let fullURL: URL
if let gets = getParams {
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
} else {
fullURL = URL(string: path, relativeTo: baseURL)!
}
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data? = nil
if let posts = postParams {
do {
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
} catch let error as NSError {
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
}
}
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
}
认证函数,使用上述函数创建的任务:
public func authorize(withCode code: String?, completion: AccessTokenExchangeCompletionHandler) {
// I have removed a lot of irrelevant code here, such as the dictionary building code, to make this snippet shorter.
let obtainTokenTask = buildPOSTTask(onURLSession: self.urlSession, appendingPath: "auth/access_token", withPostParameters: nil, getParameters: body, httpHeaders: nil) { (data, response, error) in
if let err = error {
completion(error: err)
} else {
print("Response is \(response)")
completion(error: nil)
}
}
obtainTokenTask.resume()
}
我在测试中发现了这个错误:
let testUser = Anilist(grantType: grant, name: "Test Session")
let exp = expectation(withDescription: "Waiting for authorization")
testUser.authorize(withCode: "a valid code") { (error) in
if let er = error {
XCTFail("Authentication error: \(er.localizedDescription)")
}
exp.fulfill()
}
self.waitForExpectations(withTimeout: 5) { (err) in
if let error = err {
XCTFail(error.localizedDescription)
}
}
它总是立即失败并出现此错误:
Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED,
NSLocalizedDescription=cancelled,
NSErrorFailingURLStringKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED}
请记住以下几点:
- 会话使用的URL有效。
- 所有凭据均有效。
- 它 立即 失败并出现 "cancelled" 错误,这在以前根本没有发生过。我不是在任何地方取消任务,所以它被系统取消了。
- 它在启用无限期执行的 Playgrounds 上也会失败。这不限于我的测试。
这是我尝试过的事情的列表:
- 因为我怀疑这是一个错误,所以我首先尝试清理我的项目,删除派生数据,并重置所有模拟器。 None 他们工作了。
- 甚至重新启动我的 Mac...
- 由于怀疑上传任务由于没有任何强指针而被释放,进而调用
cancel
,我也将authorize
重写为return buildPOSTTask
创建的任务,并在我的测试中将其分配给一个变量。任务仍然被取消。
我还没有尝试过的事情(但我会接受我完成这些的任何其他想法):
- 运行 它在物理设备上。当前正在 iPad 下载 iOS 10,因为这是一个 iOS 10 项目。编辑:我刚试过,但不可能这样做。
我不知道该尝试什么。生成的日志似乎没有任何有用的信息。
编辑:
我决定 post 整个项目都放在这里。不管怎样,它完成后都会开源,我得到的 API 凭据是用于测试应用程序的。
你的服务器坏了..
tcp_connection_cancel 1
nw_socket_handle_socket_event Event mask: 0x4
nw_socket_handle_socket_event Socket received WRITE_CLOSE event
nw_endpoint_handler_cancel [1 anilist.co:443 ready resolver (satisfied)]
nw_endpoint_handler_cancel [1.1 104.28.1.44:443 ready socket-flow (satisfied)]
__nw_socket_service_writes_block_invoke sendmsg(fd 9, 31 bytes): socket has been closed
nw_endpoint_flow_protocol_error [1.1
104.28.1.44:443 cancelled socket-flow (null)] Socket protocol sent error: [32] Broken pipe
nw_endpoint_flow_protocol_disconnected [1.1 104.28.1.44:443 cancelled socket-flow (null)] Output protocol disconnected
nw_endpoint_handler_cancel [1.2 104.28.0.44:443 initial path (null)]
nw_resolver_cancel_on_queue 0x60800010da40
[NWConcrete_tcp_connection dealloc] 1
[User Defaults] CFPrefsPlistSource<0x6180000f8700> (Domain: XIO.PrivateAPITest, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null)) is waiting for writes to complete so it can determine if new data is available
它无限等待 "writes" 完成..
它发送请求.. 进行 SSL 握手但没有得到响应。它超时并认为它是一个损坏的请求..
class WTF : NSObject, URLSessionDelegate {
var urlSession: URLSession!
override init() {
super.init()
urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
var request = URLRequest(url: URL(string: "https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED")!)
request.httpMethod = "POST"
let data = try! JSONSerialization.data(withJSONObject: ["Test":"Test"], options: [])
urlSession.uploadTask(with: request, from: data).resume()
}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
completionHandler(.performDefaultHandling, nil)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: NSError?) {
}
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: (URLRequest?) -> Void) {
completionHandler(request)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: (URLSession.ResponseDisposition) -> Void) {
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
}
}
在为此苦苦挣扎了 6 天之后,在谷歌上不停地搜索解决方案之后,我真的很高兴可以说我终于想通了。
原来,出于某种神秘原因,uploadTask(with:from:completionHandler)
中的 from:
参数不能为 nil。尽管该参数被标记为可选 Data
,但它在丢失时会立即被取消。这可能是 Apple 方面的一个错误,当我无法让它工作时我打开了一个错误,所以我会用这个新信息更新我的错误报告。
话虽如此,我所要做的就是更新我的 buildPOSTTask
方法,以解决传递的字典为 nil 的可能性。有了它,它现在可以正常工作了:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
let fullURL: URL
if let gets = getParams {
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
} else {
fullURL = URL(string: path, relativeTo: baseURL)!
}
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data
if let posts = postParams {
do {
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
} catch let error as NSError {
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
}
} else {
postParameters = Data()
}
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
}
您是否有机会使用 Ensighten 等第三方库?我在 XCode 8 beta 中遇到了完全相同的问题(在 XCode 7 中工作正常)并且我所有带有 nil 参数的块都导致崩溃。结果是图书馆做了一些编码导致了这个问题。
对我来说,这是一个弱引用导致了这个问题,所以我改变了
completion: { [weak self] (response: Result<ResponseType, Error>)
至
completion: { [self] (response: Result<ResponseType, Error>)
我遇到了一个奇怪的问题,新创建的 URLSessionUploadTask
会立即被取消。我不确定这是否是当前 Xcode 8.
我怀疑这可能是一个错误,因为我要 post 运行 的代码正好一次。之后没有对其进行任何更改,然后它就停止工作了。是的,它确实 运行 一次,然后就停止工作了。我将 post 接近尾声的错误。
我将 post 下面的代码,但首先我将总结一下这里的逻辑是如何工作的。
我的测试,或用户公开的 API(用于 Playgrounds 或直接在应用程序上使用的 IE),调用 authorize
方法。此 authorize
方法将依次调用 buildPOSTTask
,这将构造一个有效的 URL 和 return 一个 URLSessionUploadTask
以供 authorize
方法使用.
话虽如此,代码如下:
会话:
internal let urlSession = URLSession(configuration: .default)
创建上传任务函数:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
let fullURL: URL
if let gets = getParams {
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
} else {
fullURL = URL(string: path, relativeTo: baseURL)!
}
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data? = nil
if let posts = postParams {
do {
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
} catch let error as NSError {
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
}
}
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
}
认证函数,使用上述函数创建的任务:
public func authorize(withCode code: String?, completion: AccessTokenExchangeCompletionHandler) {
// I have removed a lot of irrelevant code here, such as the dictionary building code, to make this snippet shorter.
let obtainTokenTask = buildPOSTTask(onURLSession: self.urlSession, appendingPath: "auth/access_token", withPostParameters: nil, getParameters: body, httpHeaders: nil) { (data, response, error) in
if let err = error {
completion(error: err)
} else {
print("Response is \(response)")
completion(error: nil)
}
}
obtainTokenTask.resume()
}
我在测试中发现了这个错误:
let testUser = Anilist(grantType: grant, name: "Test Session")
let exp = expectation(withDescription: "Waiting for authorization")
testUser.authorize(withCode: "a valid code") { (error) in
if let er = error {
XCTFail("Authentication error: \(er.localizedDescription)")
}
exp.fulfill()
}
self.waitForExpectations(withTimeout: 5) { (err) in
if let error = err {
XCTFail(error.localizedDescription)
}
}
它总是立即失败并出现此错误:
Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED, NSLocalizedDescription=cancelled, NSErrorFailingURLStringKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED}
请记住以下几点:
- 会话使用的URL有效。
- 所有凭据均有效。
- 它 立即 失败并出现 "cancelled" 错误,这在以前根本没有发生过。我不是在任何地方取消任务,所以它被系统取消了。
- 它在启用无限期执行的 Playgrounds 上也会失败。这不限于我的测试。
这是我尝试过的事情的列表:
- 因为我怀疑这是一个错误,所以我首先尝试清理我的项目,删除派生数据,并重置所有模拟器。 None 他们工作了。
- 甚至重新启动我的 Mac...
- 由于怀疑上传任务由于没有任何强指针而被释放,进而调用
cancel
,我也将authorize
重写为returnbuildPOSTTask
创建的任务,并在我的测试中将其分配给一个变量。任务仍然被取消。
我还没有尝试过的事情(但我会接受我完成这些的任何其他想法):
- 运行 它在物理设备上。当前正在 iPad 下载 iOS 10,因为这是一个 iOS 10 项目。编辑:我刚试过,但不可能这样做。
我不知道该尝试什么。生成的日志似乎没有任何有用的信息。
编辑:
我决定 post 整个项目都放在这里。不管怎样,它完成后都会开源,我得到的 API 凭据是用于测试应用程序的。
你的服务器坏了..
tcp_connection_cancel 1
nw_socket_handle_socket_event Event mask: 0x4
nw_socket_handle_socket_event Socket received WRITE_CLOSE event
nw_endpoint_handler_cancel [1 anilist.co:443 ready resolver (satisfied)]
nw_endpoint_handler_cancel [1.1 104.28.1.44:443 ready socket-flow (satisfied)]
__nw_socket_service_writes_block_invoke sendmsg(fd 9, 31 bytes): socket has been closed
nw_endpoint_flow_protocol_error [1.1
104.28.1.44:443 cancelled socket-flow (null)] Socket protocol sent error: [32] Broken pipe
nw_endpoint_flow_protocol_disconnected [1.1 104.28.1.44:443 cancelled socket-flow (null)] Output protocol disconnected
nw_endpoint_handler_cancel [1.2 104.28.0.44:443 initial path (null)]
nw_resolver_cancel_on_queue 0x60800010da40
[NWConcrete_tcp_connection dealloc] 1
[User Defaults] CFPrefsPlistSource<0x6180000f8700> (Domain: XIO.PrivateAPITest, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null)) is waiting for writes to complete so it can determine if new data is available
它无限等待 "writes" 完成..
它发送请求.. 进行 SSL 握手但没有得到响应。它超时并认为它是一个损坏的请求..
class WTF : NSObject, URLSessionDelegate {
var urlSession: URLSession!
override init() {
super.init()
urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
var request = URLRequest(url: URL(string: "https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED")!)
request.httpMethod = "POST"
let data = try! JSONSerialization.data(withJSONObject: ["Test":"Test"], options: [])
urlSession.uploadTask(with: request, from: data).resume()
}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
completionHandler(.performDefaultHandling, nil)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: NSError?) {
}
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: (URLRequest?) -> Void) {
completionHandler(request)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: (URLSession.ResponseDisposition) -> Void) {
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
}
}
在为此苦苦挣扎了 6 天之后,在谷歌上不停地搜索解决方案之后,我真的很高兴可以说我终于想通了。
原来,出于某种神秘原因,uploadTask(with:from:completionHandler)
中的 from:
参数不能为 nil。尽管该参数被标记为可选 Data
,但它在丢失时会立即被取消。这可能是 Apple 方面的一个错误,当我无法让它工作时我打开了一个错误,所以我会用这个新信息更新我的错误报告。
话虽如此,我所要做的就是更新我的 buildPOSTTask
方法,以解决传递的字典为 nil 的可能性。有了它,它现在可以正常工作了:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
let fullURL: URL
if let gets = getParams {
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
} else {
fullURL = URL(string: path, relativeTo: baseURL)!
}
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data
if let posts = postParams {
do {
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
} catch let error as NSError {
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
}
} else {
postParameters = Data()
}
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
}
您是否有机会使用 Ensighten 等第三方库?我在 XCode 8 beta 中遇到了完全相同的问题(在 XCode 7 中工作正常)并且我所有带有 nil 参数的块都导致崩溃。结果是图书馆做了一些编码导致了这个问题。
对我来说,这是一个弱引用导致了这个问题,所以我改变了
completion: { [weak self] (response: Result<ResponseType, Error>)
至
completion: { [self] (response: Result<ResponseType, Error>)