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

support redirect requests natively #808

Merged
merged 29 commits into from Jul 17, 2015
Merged

support redirect requests natively #808

merged 29 commits into from Jul 17, 2015

Conversation

shiftkey
Copy link
Member

Fixes #798
Fixes #748
Fixes #812

Building on the awesome start that @darrelmiller started over in darrelmiller@e5d7d6e

  • cry forever, then get up and ponder on what to do with all the suppressed warnings about incorrect disposal
  • write integration tests for redirecting repositories:
    • https://github.com/robconery/massive -> https://github.com/fbouma/massive
  • write some tests around creating data and handling the 307
    • setup a public repository to test this
  • RepositoryContentsClient.GetArchiveLink depends on not following the redirect, prolly need to address this
  • documentation
  • set the timeout for downloading the archive above the default

@jasonrudolph anything I've missed here?

@darrelmiller
Copy link
Contributor

I just noticed the RedirectHandler CopyResult uses a .Result. That's because I pulled the code from a .net 4.0 project. It should probably be fixed to use async/await. I'd do it but I'm not sure how to add a commit to a PR.

@jasonrudolph
Copy link

Thanks for digging into this, @shiftkey. ⚡

@jasonrudolph anything I've missed here?

My C# leaves much to be desired, but @kdaigle and I sketched out this plaintext spec that might help answer your question:

  • MUST follow redirect requests if HTTP status code is 301, 302, or 307. (These are the redirect codes actively used by the GitHub API.)
  • MUST redirect a 30x status code if the HTTP method is HEAD, OPTIONS, GET, POST, PUT, PATCH, or DELETE.
  • MUST use the original request's HTTP method when following a redirect where the HTTP status code is 307.
  • SHOULD use the original request's HTTP method when following a redirect where the HTTP status code is 301 or 302.
  • MUST use the original request's authentication credentials when following a redirect where the original host matches the redirect host.
  • MUST NOT use the original request's authentication credentials when following a redirect where the original host does not match the redirect host.
  • SHOULD only follow 3 redirects.

Does that help?

@darrelmiller
Copy link
Contributor

  1. I'm not doing redirect counting to limit redirects. I can fix that.
  2. I'm not checking host before copying auth header. I can fix that.
  3. Currently I change the method on a 301 or 302 because it is allowed. Changing the behaviour to not change the method is easy enough.

@darrelmiller
Copy link
Contributor

I have done all the fixes to the RedirectHandler and added a bunch of tests for it. However, I have absolutely no @!#$@#$ clue how to push the changes up to anywhere on Github.
I pulled down the pr/808 branch and made the changes there, but am unable to push that branch with the fixes to my fork.

@shiftkey
Copy link
Member Author

@darrelmiller if you're working on your fork and it's in a branch I know about, I can pull in your changes into this PR...

@darrelmiller
Copy link
Contributor

@shiftkey I don't really know how. But I succeeded in pushing my changes to my fork. darrelmiller@cf9c845

@darrelmiller
Copy link
Contributor

Couple of comments:

The 301 and 302 behaviour is now different to what HttpWebRequest does by default. But the new behaviour is what was originally intended for 301/302. (Possibly the most useful diagram I have ever seen on this subject is here https://tools.ietf.org/html/rfc7238#section-1)

Keeping the auth header around based on host is consistent with what the .Net CredentialCache does. However, based on my interpretation of the HTTP specs, it should really be based on the realm. Which would require a 401 dance to find out the new realm. I would file this under "nice idea in theory but no-one does it". The pedant in me felt the need to mention it.

@jasonrudolph
Copy link

The 301 and 302 behaviour is now different to what HttpWebRequest does by default. But the new behaviour is what was originally intended for 301/302. (Possibly the most useful diagram I have ever seen on this subject is here https://tools.ietf.org/html/rfc7238#section-1)

@darrelmiller: Thanks for linking to that diagram. Wonderfully succinct and useful. ⚡

@shiftkey
Copy link
Member Author

@darrelmiller your latest changes are applied onto this PR - thanks for that 💖.

i'll look at those failing tests, and putting together some additional tests for the GitHub API.

@@ -109,6 +103,8 @@ protected virtual HttpRequestMessage BuildRequestMessage(IRequest request)
{
var fullUri = new Uri(request.BaseAddress, request.Endpoint);
requestMessage = new HttpRequestMessage(request.Method, fullUri);

requestMessage.Properties["AllowAutoRedirect"] = request.AllowAutoRedirect;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@darrelmiller I'm almost inclined to obsolete request.AllowAutoRedirect and simplify things - as we need to control this behaviour in our very specific way. Any objections?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It felt like a nice feature to have, but I'm struggling to think of a scenario where I would actually need it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was one of the first things I'd removed when refactoring earlier attempts. Good enough for me 🔥🔥🔥🔥🔥

@shiftkey
Copy link
Member Author

Just added a passing test against the new redirects - I'll add some more later this week, but this is feeling rather close...

@darrelmiller
Copy link
Contributor

👍

@shiftkey
Copy link
Member Author

shiftkey commented Jun 5, 2015

Added in a test for issue creation and finding a repository. Ready for review.

@shiftkey
Copy link
Member Author

Just added one more little task before I'm happy with this.

Conflicts:
	Octokit/Octokit-Mono.csproj
	Octokit/Octokit-MonoAndroid.csproj
	Octokit/Octokit-Monotouch.csproj
	Octokit/Octokit-Portable.csproj
	Octokit/Octokit-netcore45.csproj
@shiftkey
Copy link
Member Author

Extracted #817 rather than drag this out any longer.

Ready for review 🐶

@shiftkey
Copy link
Member Author

@haacked would love some 👀 on this before I merge it and continue down the path of extracting more HTTP-friendly abstractions...

@darrelmiller
Copy link
Contributor

Just one observation that would probably have been much more useful before we started all this.

If you are actually using the CredentialCache with credentials associated to the correct origin server, then when you redirect to a URL with the same host then the credentials are carried over. Using a credential cache avoids setting creds in a default header and takes care of applying the correct credentials on every request.

Maybe it shouldn't be the responsibility of redirection code to be carrying over the authorization header.

@shiftkey
Copy link
Member Author

If you are actually using the CredentialCache with credentials associated to the correct origin server, then when you redirect to a URL with the same host then the credentials are carried over.

Yep, I'd like to address this at some point - we should mimic this behaviour in our abstraction and move it into HttpClient.

@darrelmiller
Copy link
Contributor

Funny you should mention that, but I just finished writing an AuthenticationService/HttpCredentialCache that plugs into the message handler pipeline. It's not on Github yet though.

@shiftkey shiftkey mentioned this pull request Jul 3, 2015
@shiftkey
Copy link
Member Author

@haacked bump for code review plz

Not worth commenting on in a code review, so I fixed it myself.
var redirectCount = 0;
if (request.Properties.Keys.Contains(RedirectCountKey))
{
redirectCount = (int)request.Properties[RedirectCountKey];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this fail if there's no value here? Or do we always seed this with a value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would only fail if someone pre-filled the property with a value that cannot be cast to an int. A value is assigned to the property when it is initially created, right after this If ends.

@haacked
Copy link
Contributor

haacked commented Jul 17, 2015

Looks good to me. Just one question.

haacked added a commit that referenced this pull request Jul 17, 2015
support redirect requests natively
@haacked haacked merged commit 795aff1 into master Jul 17, 2015
@haacked haacked deleted the redirect-requests branch July 17, 2015 17:46
@jasonrudolph
Copy link

@darrelmiller @shiftkey: 👏 🌟

@shiftkey
Copy link
Member Author

I'll run through the integration tests and see if I can put together a 🚢 on Monday (my time).

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