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

request.HTTPBody is always nil in custom matcher #32

Closed
paulyoung opened this issue Oct 22, 2015 · 16 comments
Closed

request.HTTPBody is always nil in custom matcher #32

paulyoung opened this issue Oct 22, 2015 · 16 comments

Comments

@paulyoung
Copy link

I'm setting the HTTPBody property of an NSMutableURLRequest and trying to do some comparison on it inside of a custom matcher.

However, the property's value is always nil by the time the request reaches the matcher despite it being set prior to that.

@kylef
Copy link
Owner

kylef commented Oct 22, 2015

Hi @paulyoung, just to confirm. you're setting the property and then making a request with NSURLConnection/NSURLSession?

Would you be able to check if passing your request directly to MockingjayProtocol's canInitWithRequest method results in the HTTPBody still being there.

MockingjayProtocol.canInitWithRequest(request)

@paulyoung
Copy link
Author

@kylef I can confirm that inside of canInitWithRequest(request) the HTTPBody is nil.

However, I just made this change and now everything works as expected: the-grid/Disc@02b31d9

In any case, this issue can be closed. Thanks for looking into it!

@paulyoung
Copy link
Author

So it appears that this issue wasn't actually resolved. I think what was happening was the actual request was being made, and now that request is no longer valid so a test started failing.

Where session is NSURLSession.sharedSession(), here's some info from right before the request is passed as session.dataTaskWithRequest(request):

(lldb) po request
<NSMutableURLRequest: 0x100723390> { URL: https://passport.thegrid.io/login/authorize/token }

(lldb) po request.HTTPBody
▿ Optional(<7b22636f 6465223a 22636f64 65222c22 636c6965 6e745f69 64223a22 6964222c 22636c69 656e745f 73656372 6574223a 22736563 72657422 2c226772 616e745f 74797065 223a2261 7574686f 72697a61 74696f6e 5f636f64 65227d>)

Here's the same thing inside my custom matcher:

(lldb) po request
<NSURLRequest: 0x102046500> { URL: https://passport.thegrid.io/login/authorize/token }

(lldb) po request.HTTPBody
nil

@kylef
Copy link
Owner

kylef commented Oct 24, 2015

Thats for the additional information.

Had a look into this and I can confirm this is how it's behaving, after doing some digging it does seem like either an bug in NSURLSession or how NSURLSession is designed. Mockingjay registers as an NSURLProtocol and the first method that NSURLSession will call on the protocol is canInitWithRequest: to try and find the protocol that can handle this request.

I've also found this note from a README from OHHTTPStubs (the popular Objective-C alternative) which mentions the same problem. I've found a related discussion inside OHHTTPStubs:

This leads to NSURLProtocol's +canInitWithRequest: method to be called with a request with an empty HTTPBody, because HTTPBody and HTTPBodyStream properties are handled internally and specifically by Apple for the HTTP protocol (for the specific internal NSURLProtocol originally managing the "http://" scheme by Apple).

I'm not really sure how we can go about adding support for this.

@kylef
Copy link
Owner

kylef commented Oct 24, 2015

Additionally, found a related rdar://15993891 which includes a comment stating that this is not how the documentation says it should work:

Worth nothing that Apple have demonstrated this field should be available in their example project:
https://developer.apple.com/library/ios/samplecode/CustomHTTPProtocol/Listings/CustomHTTPProtocol_Core_Code_CanonicalRequest_m.html#//apple_ref/doc/uid/DTS40013653-CustomHTTPProtocol_Core_Code_CanonicalRequest_m-DontLinkElementID_8

@kylef
Copy link
Owner

kylef commented Oct 24, 2015

@paulyoung
Copy link
Author

Thanks again for looking into this! I employed the "transparent" workaround.

Feel free to close this issue or leave open as you see fit.

@kylef kylef closed this as completed Nov 19, 2015
@paulyoung paulyoung changed the title request.HTTP is always nil in custom matcher request.HTTPBody is always nil in custom matcher Jan 12, 2016
markspanbroek added a commit to Charterhouse/Shhwift that referenced this issue Jul 6, 2016
@yoavst
Copy link

yoavst commented Aug 18, 2016

@kylef , can you add a property that uses that method as an extension, or at least add this issue to README? I've spent an hour thinking the problem is in my sending code :)

@blackm00n
Copy link
Contributor

I'm using the following workaround (Swift 2.3)

The usage is the following:

func test() {
    let hack = HttpBodyHack()

    let matcher: Matcher = { request in
        let data = request.httpBodyHack()
        // use data
    }

    // make request

    waitForExpectationsWithTimeout(1) { _ in
        hack
    }
}

I can make PR if the solution is good enough. @kylef

blackm00n added a commit to blackm00n/Mockingjay that referenced this issue Dec 5, 2016
@jpalten
Copy link

jpalten commented Jan 20, 2017

Seems not to work on my machine. It swizzles alright, but the body is nil in the request again. Perhaps it is copied to a new instance that leaves the body empty.

@blackm00n
Copy link
Contributor

@jpalten are you talking about #74 ?

@craftedbymax
Copy link

Works perfectly using httpBodyStream with this extension (updated to Swift 3):

import Foundation

extension InputStream {
    func readfully() -> Data {
        var result = Data()
        var buffer = [UInt8](repeating: 0, count: 4096)
        
        open()
        
        var amount = 0
        repeat {
            amount = read(&buffer, maxLength: buffer.count)
            if amount > 0 {
                result.append(buffer, count: amount)
            }
        } while amount > 0
        
        close()
        
        return result
    }
}

Usage:

let bodyData = request.httpBodyStream?.readfully()

@ychongsaytc
Copy link

Works perfectly using httpBodyStream with this extension (updated to Swift 3):

Doesn't work anymore.

@vinczebalazs
Copy link

Works perfectly using httpBodyStream with this extension (updated to Swift 3):

Doesn't work anymore.

Works for me (Swift 5.1).

@Adrimi
Copy link

Adrimi commented May 17, 2021

Works as intended (Swift 5.4)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants