Repository: jupyterhub/jupyterhub

Issue:
Query strings are lost in between /user/redirect/ and /user/my-username/
I hoped to use [nbgitpuller](https://github.com/data-8/nbgitpuller) in order to be able to write something like `https://my-z2jh-deployment.com/user/redirect/git-pull?repo=my-repo-url` in order to first have the user login, and then do something specific, such as triggering nbgitpuller.

This would had worked fine if I wanted to do something that didn't require a query string in the url, like: `https://my-z2jh-deployment.com/user/redirect/terminals/1`.

But... Query strings are lost in between the redirection from `/user/redirect/` to `/user/my-username/` :'(

![query-truncated](https://user-images.githubusercontent.com/3837114/38176144-e88abbd6-35e9-11e8-93bf-014a0f0da261.gif)

### Update 1
I think of this as a bug, because I guess this is not the indented behavior.


Discussion:
@consideRatio Do you think this is a bug or a suggestion or a different behavior that you would like to see in the future?
I just discovered that...
* `/hub/user-redirect/path?with=querystrings` links works fine.
* `/user/redirect/path?with=querystrings` links truncates the querystring.

Should we really have two two different routes? I presume are meant to do the same thing.
Hi @consideRatio,

Interesting...

Do you have the error log from the one that truncates? Does it redirect correctly otherwise? 

Trying to see if this is related at all to #1783. Thanks.
@willingc one request caused this in the hub pod's logs

```
[I 2018-04-09 19:05:48.761 JupyterHub log:122] 302 GET /user/redirect/git-pull?repo=redirtest → /hub/user/redirect/git-pull?repo=redirtest (@10.156.0.3) 0.79ms
[I 2018-04-09 19:05:48.803 JupyterHub log:122] 302 GET /hub/user/redirect/git-pull?repo=redirtest → /user/erik.sundell/git-pull (erik.sundell@10.156.0.3) 2.78ms
```
Oh... Hmmm... Notice that there is `/hub/user-redirect/` and `/hub/user/redirect/` involved here...
I think there is a bug, where /user/redirect redirects to /hub/user/redirect instead of /hub/user-redirect/.


```
# /hub/user/redirect causes issues, and we can arrive here with /user/redirect as well
[I 2018-04-09 19:11:07.385 JupyterHub log:122] 302 GET /hub/user/redirect/git-pull?repo=testing → /user/erik.sundell/git-pull (erik.sundell@10.156.0.3) 2.57ms

# /hub/user-redirect works fine
[I 2018-04-09 19:13:11.149 JupyterHub log:122] 302 GET /hub/user-redirect/git-pull?repo=testing → /user/erik.sundell/git-pull?repo=testing (erik.sundell@10.156.0.3) 3.25ms
```
Just to clarify this is running 0.8.1 not master. Correct?

@willingc yepp!
@minrk Is this due to an error in `UserSpawnHandler` or `UserRedirectSpawnHandler`? I'm not quite sure when one is used over the other.

@consideRatio Would you mind going back to the logs and look at the lines preceding the log errors and perhaps post those as well. Thanks.


@willingc sure coming up in a few minutes


@willingc - I started a new singleuser pod, then made three webrequests which you can spot by searching in the logs for `test1`, `test2` and `test3`. They are all in the end.

```
[I 2018-04-09 22:48:19.194 JupyterHub base:727] erik.sundell is pending spawn
[I 2018-04-09 22:48:20.941 JupyterHub log:122] 200 GET /hub/api (@10.16.13.3) 0.69ms
[I 2018-04-09 22:48:21.058 JupyterHub base:447] User erik.sundell took 198.682 seconds to start
[I 2018-04-09 22:48:21.060 JupyterHub proxy:231] Adding user erik.sundell to proxy /user/erik.sundell/ => http://10.16.13.3:8888
[I 2018-04-09 22:48:26.203 JupyterHub log:122] 302 GET /hub/user/erik.sundell/ → /user/erik.sundell/?redirects=1 (erik.sundell@10.16.2.1) 8.50ms
[I 2018-04-09 22:48:26.342 JupyterHub log:122] 302 GET /hub/api/oauth2/authorize?client_id=user-erik.sundell&redirect_uri=%2Fuser%2Ferik.sundell%2Foauth_callback&response_type=code&state=asdfasdfasdf → /user/erik.sundell/oauth_callback?code=asdfasdf&state=asdfasdfasdfasdf (erik.sundell@10.16.2.1) 7.97ms
[I 2018-04-09 22:48:26.409 JupyterHub log:122] 200 POST /hub/api/oauth2/token (erik.sundell@10.16.13.3) 20.20ms
[I 2018-04-09 22:48:26.415 JupyterHub log:122] 200 GET /hub/api/authorizations/token/[secret] (erik.sundell@10.16.13.3) 4.02ms
[I 2018-04-09 22:48:26.466 JupyterHub log:122] 200 GET /hub/api/authorizations/token/[secret] (erik.sundell@10.16.13.3) 4.10ms
[I 2018-04-09 22:48:27.345 JupyterHub log:122] 200 GET /hub/api/authorizations/token/[secret] (erik.sundell@10.16.13.3) 2.96ms
[I 2018-04-09 22:48:27.374 JupyterHub log:122] 302 GET /iframes → /hub/iframes (@10.16.2.1) 0.66ms
[W 2018-04-09 22:48:27.430 JupyterHub log:122] 404 GET /hub/iframes (erik.sundell@10.16.2.1) 3.00ms
[I 2018-04-09 22:48:29.818 JupyterHub log:122] 200 GET /hub/api/authorizations/token/[secret] (erik.sundell@10.16.13.3) 7.76ms
[I 2018-04-09 22:50:58.390 JupyterHub log:122] 302 GET /user/redirect/git-pull?repo=test1 → /hub/user/redirect/git-pull?repo=test1 (@10.16.2.1) 0.79ms
[I 2018-04-09 22:50:58.445 JupyterHub log:122] 302 GET /hub/user/redirect/git-pull?repo=test1 → /user/erik.sundell/git-pull (erik.sundell@10.16.2.1) 2.46ms
[I 2018-04-09 22:51:48.999 JupyterHub log:122] 302 GET /hub/user-redirect/git-pull?repo=test2 → /user/erik.sundell/git-pull?repo=test2 (erik.sundell@10.16.2.1) 4.27ms
[I 2018-04-09 22:52:03.143 JupyterHub log:122] 302 GET /hub/user/redirect/git-pull?repo=test3 → /user/erik.sundell/git-pull (erik.sundell@10.16.2.1) 2.42ms
```
`/hub/user/redirect` and `/hub/user-redirect/` mean different things. `/user/redirect` means that you are asking to access the server of the user named 'redirect'. This is most likely to happen when users copy a URL from their browser and share it with another user. JupyterHub tries to *guess* what the right thing to do is if you give a link to a user who doesn't have access to your server. It is rather complicated because there are a variety of cases:

1. `redirect` server may be running or not (initial request handled by user server or Hub's PrefixRedirectHandler or the server running at `/user/redirect`
2. visitor may be logged in or not
3. visitor may have access to `/user/redirect` server or not

The first two determine the redirect sequence of Handlers that will need to pass around the URL and ensure it stays correct. The third actually determines what the right answer is for the final URL (i.e. the URL should be unchanged if you have access to it, but substitute your server if you don't).

This complication is why `/hub/user-redirect` exists. It is a dedicated URL for sharing links, and is what you should use if sharing links to users' own servers is your goal and you are aware of it.

I think ultimately the bug you are running directly into is that [PrefixRedirectHandler](https://github.com/jupyterhub/jupyterhub/blob/0.8.1/jupyterhub/handlers/base.py#L645) doesn't preserve the URL query in the redireect. This is a bug we should fix. [UserRedirectHandler](https://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/handlers/base.py#L1004) has specific processing of request.query, which is what needs to be added to PrefixRedirectHandler. Would you like to make a PR to add the same query handling?
@minrk ah so writing `/user/redirect` is probably something i ended up doing for no good reason because i got confused, and I should write `/hub/user-redirect/` instead.

I'll work on a PR to fix `PrefixRedirectHandler` in a similar way to `UserRedirectHandler`, it would make for a good reason for me for me to learn more about JupyterHub's inner working.

@minrk @willingc thanks for the help understanding this!
I'm currently stuck setting up a JupyterHub development #1789

If anyone wants to fix what @minrk suggests, go for it!

> I think ultimately the bug you are running directly into is that PrefixRedirectHandler doesn't preserve the URL query in the redireect. This is a bug we should fix. UserRedirectHandler has specific processing of request.query, which is what needs to be added to PrefixRedirectHandler. Would you like to make a PR to add the same query handling?

Resolving patch:
diff --git a/jupyterhub/handlers/base.py b/jupyterhub/handlers/base.py
--- a/jupyterhub/handlers/base.py
+++ b/jupyterhub/handlers/base.py
@@ -985,6 +985,9 @@ async def get(self, name, user_path):
             # logged in as a different user, redirect
             self.statsd.incr('redirects.user_to_user', 1)
             target = url_path_join(current_user.url, user_path or '')
+            if self.request.query:
+                # FIXME: use urlunparse instead?
+                target += '?' + self.request.query
             self.redirect(target)
         else:
             # not logged in, clear any cookies and reload


Test patch: