Why is the HTTPBody of a request inside an NSURLProtocol Subclass always nil?

冷暖自知 提交于 2019-12-01 07:40:43

问题


I have my NSURLProtocol MyGreatProtocol. I add it to the URL Loading system,

NSURLProtocol.registerClass(MyGreatProtocol)

I then start receiving events inside MyGreatProtocol during network sessions.

Suppose I create the session after registering my protocol,

let session = NSURLSession.sharedSession()
    let request = NSMutableURLRequest(URL: NSURL(string:"http://example.com")!, cachePolicy: .ReloadIgnoringLocalCacheData, timeoutInterval: 2.0) //arbitrary 404
    request.HTTPBody = ([1, 3, 3, 7] as [UInt8]).toNSData
    print("body data: \(request.HTTPBody)") //prints `Optional(<01030307>)`
    request.HTTPMethod = "POST"
    session.dataTaskWithRequest(request) { (data, response, err) in
        print("data: \(data)\nresponse: \(response)\nerror\(err)")
    }.resume()

I expect the request's HTTP body 1/3/3/7 to be present inside MyGreatProtocol, where it is not.

Inside MyGreatProtocol, I override the following methods.

override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest {
    print(request.HTTPBody) //prints nil
    return request
}

override class func canInitWithRequest(request: NSURLRequest) -> Bool {
    print(request.HTTPBody) //prints nil
    return true
}

override func startLoading() {
    print(self.request.HTTPBody) //prints nil
}

The other properties of the NSURLRequest seem to be retained. The URL, HTTP verb, headers, etc, are all there. Something specific about the nature of the body remains elusive.

Why is the HTTP Body Nil inside a custom NSURLProtocol?

There seems to be some similar discussion on radars previously filed (i.e. https://bugs.webkit.org/show_bug.cgi?id=137299)


回答1:


Here's a sample code for reading the httpBody:

Swift 5

extension URLRequest {

    func bodySteamAsJSON() -> Any? {

        guard let bodyStream = self.httpBodyStream else { return nil }

        bodyStream.open()

        // Will read 16 chars per iteration. Can use bigger buffer if needed
        let bufferSize: Int = 16

        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)

        var dat = Data()

        while bodyStream.hasBytesAvailable {

            let readDat = bodyStream.read(buffer, maxLength: bufferSize)
            dat.append(buffer, count: readDat)
        }

        buffer.deallocate()

        bodyStream.close()

        do {
            return try JSONSerialization.jsonObject(with: dat, options: JSONSerialization.ReadingOptions.allowFragments)
        } catch {

            print(error.localizedDescription)

            return nil
        }
    }
}

Then, in URLProtocol:

override func startLoading() {

    ...

    if let jsonBody = self.request.bodySteamAsJSON() {

        print("JSON \(jsonBody)")

    }

    ...
}



回答2:


IIRC, body data objects get transparently converted into streaming-style bodies by the URL loading system before they reach you. If you need to read the data:

  • Open the HTTPBodyStream object
  • Read the body data from it

There is one caveat: the stream may not be rewindable, so don't pass that request object on to any other code that would need to access the body afterwards. Unfortunately, there is no mechanism for requesting a new body stream, either (see the README file from the CustomHTTPProtocol sample code project on Apple's website for other limitations).



来源:https://stackoverflow.com/questions/36555018/why-is-the-httpbody-of-a-request-inside-an-nsurlprotocol-subclass-always-nil

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!