-
Notifications
You must be signed in to change notification settings - Fork 601
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
Fix redirect race condition #224
Fix redirect race condition #224
Conversation
The redirect and response finished callbacks were being executed in two separate blocks, each submitted using dispatch_after. The wait time for each dispatch_after block was equal (and potentially 0 if the user did not configure a request time). As a result, the order the blocks ran in was non-deterministic and unpredictable. This broke the strong ordering of the callbacks. When the response finished callback arrived before the redirect callback, the OHHTTPStubs client would be given the contents of the 3xx response with no opportunity to follow the redirect. By moving both callbacks into a single block, strong ordering is again guaranteed and redirect callbacks always happen when appropriate. For more details about the required ordering of NSURLProtocolClient callbacks, see the "Threading Notes" section of the readme of the Apple CustomHTTPProtocol sample code project: https://developer.apple.com/library/content/samplecode/CustomHTTPProtocol/Listings/Read_Me_About_CustomHTTPProtocol_txt.html
Thanks for the PR! Very good point and good explanation 👍 I'm wondering if, when I did write that code (potentially half asleep 😂) maybe my intent was to wait for the delay to call the redirect callback… then wait for the delay again after that callback fire the real response (as a 3xx redirect + the call to the redirected URL could be seen as two requests interest of one = wouldn't we be supposed to fake twice the request time?). So maybe my intent was to send those one after the other and not in parallel (Obviously if I wanted to implement exactly that I should have nested the What do you think would be the best behavior users would expect?
I personally think I prefer solution 2 but that would break the timing compared to current version, so people who already wrote code with some delays for their stubs with redirection will see that overall delay doubled. While your solution fix just the race condition while not changing the overall delay but it seems strange to me timing-wise to have the finish closure called immediately after the redirect, like the redirection took no time… so solution 3 seems like a good mitigation? |
I'm not sure I understand the build failure. Is it a known issue? I have not tested non-zero response times, and my understanding of NSURLProtocol is limited. (There's not very much documentation.) However, I believe that this implementation is correct for non-zero response times. The response complete callback that gets called immediately after the redirect callback signals the completion of the 3xx response. Since the redirect and the final request are separate invocations of For context, the issue was introduced as part of a bug fix for a related issue in May in PR #175 . Before that fix was merged, It would be possible to write a unit test with a long response time for both the initial 3xx and the final response and verify that the response time is honored in each case with these changes. If you can wait a day or two, I'd be happy to write one. |
Indeed, makes sense; your implementation seems to be the one making more sense in that context then 👍 About the build failure, yes it's an issue I've introduced in a quite recent commit on master while trying to disable timing tests on CI, because timing tests depend highly on the speed and CPU of the machine running them — so they are non-deterministic — and typically VMs on Travis share a common CPU — meaning that timers to will be late. All of which made those timing tests (and Travis builds) fail randomly depending on VM speed and execution conditions. |
OK! Meanwhile, I will find time to write a test verifying that the new behavior is correct for non-zero request times. |
Previously, the stub response time was not honored when the stub had zero-length data.
Adds very rough timing to ensure that both request and response delays are honored in redirect scenarios. Also corrects a bug with completion blocks being called before expectations had completed.
While working on unit tests for redirect response times, I found another bug which I addressed in commit a6f5453. The response time was not being honored for requests with zero-length data. I added some very rough timing to the NSURLSession tests to verify that the amount of time spent waiting for the requests to finish was greater than or equal to the configured request and response delays. Let me know if the tests look okay. I know that using NSDate for timing isn't ideal, but I thought it simplest in this scenario. |
I'm not sur I see a better way to test that, so I'm ok with BTW, don't forget to add an entry in th |
OK! Should I add that to this PR? |
@morrowa yeah, just add a commit with the changes in the CHANGELOG, that way it'll be included automatically when we merge the PR 😃 |
@@ -6,6 +6,9 @@ | |||
* Improved documentation about `dynamicType:` vs `type(of:)`. | |||
[Antondomashnev](https://github.com/Antondomashnev) | |||
[#221](https://github.com/AliSoftware/OHHTTPStubs/pull/221) | |||
* Fixed a race condition that occasionally prevented redirect callbacks. Fixed response timing for zero-length stub data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm being pedantic, I think it would read more clearly with two separate entries/bullet points instead of one 😝
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK! Sounds good! Only one copy of the links though, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmmh, good question …
nah I think it would be more logical to put the links on both, because if anyone is interested only in one of the entries they have to have the links to that PR always right next to it (and otherwise a reader would have no clue if the second entry just miss the links because we forgot them or if they're supposed to be the same as the previous entry…)
Damn I have to stop being perfectionist and pedantic, sorry 😂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I appreciate this help! I haven't contributed to an open-source project in a long time and want to be sure that all of my changes fit the project perfectly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌
Now I'll probably have to take some time to understand what I did that makes the build fall on travis due to this "cannot import module" error, try and fix that on master so you can rebase on top on it, and hopefully this PR's build will pass at last and be mergeable at last! |
Travis failure fixed on master 🎉 |
The redirect and response finished callbacks were being executed in two separate blocks, each submitted using dispatch_after. The wait time for each dispatch_after block was equal (and potentially 0 if the user did not configure a request time). As a result, the order the blocks ran in was non-deterministic and unpredictable. This broke the strong ordering of the callbacks. When the response finished callback arrived before the redirect callback, the OHHTTPStubs client would be given the contents of the 3xx response with no opportunity to follow the redirect.
By moving both callbacks into a single block, strong ordering is again guaranteed and redirect callbacks always happen when appropriate.
For more details about the required ordering of NSURLProtocolClient callbacks, see the "Threading Notes" section of the readme of the Apple CustomHTTPProtocol sample code project:
https://developer.apple.com/library/content/samplecode/CustomHTTPProtocol/Listings/Read_Me_About_CustomHTTPProtocol_txt.html