8000 Set HTTP upgrade headers when hijacking connection by yadutaf · Pull Request #9652 · moby/moby · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Set HTTP upgrade headers when hijacking connection #9652

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

Merged
merged 3 commits into from
Dec 17, 2014
Merged

Conversation

yadutaf
Copy link
Contributor
@yadutaf yadutaf commented Dec 13, 2014

When setting up a reverse proxy like Nginx behind Docker client and Docker remote API most commands works fine except docker attach and docker exec. When these commands are used, docker "hijacks" the HTTP connection back to raw bi-directional TCP. As the reverse proxy was expecting plain HTTP it is completely lost.

This Pull Request adds Connection: Upgrade and Upgrade: tcp HTTP headers as well as return code of 101 UPGRADED in this scenario. It is fully backward compatible and, while 'tcp' upgrade is not standard, it is enough to trigger Nginx connection upgrade path as used by websockets.

Signed-off-by: Jean-Tiare Le Bigot <jt@yadutaf.fr>
@SvenDowideit
Copy link
Contributor

nice!

@jessfraz
Copy link
Contributor

LGTM but I feel like @icecrime would know for sure because he just fixed something with TLS and hijack :)

@tiborvass
Copy link
Contributor

@yadutaf would you be able to provide us with a simple way of testing this? :)

@LK4D4
Copy link
Contributor
LK4D4 commented Dec 15, 2014

At least we should try this with docker-py, status code can be hardcoded in clients.

@icecrime
Copy link
Contributor

Sounds perfectly reasonable, my only remark would be the following: maybe we should check for the Upgrade header on the server side to avoid sending a 101 for clients who didn't ask for it?

@yadutaf
Copy link
Contributor Author
yadutaf commented Dec 15, 2014

@tiborvass You can easily test this PR with an Nginx config file looking like:

worker_processes  1; 

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen 9000;

        location / {
            proxy_buffering off;
            proxy_set_header Upgrade $http_upgrade;
            proxy_pass http://localhost:8080;
        }  
    }

}

With Docker daemon listening on port 8080: docker -d -D -H tcp://localhost:8080 and DOCKER_HOST=tcp://localhost:9000.

@icecrime Sounds like a good idea to check for the Upgrade header in request. I added a tentative check, can you review this? This is my first time with Go, it may not be the best way to do it.

@@ -887,7 +887,12 @@ func postContainersAttach(eng *engine.Engine, version version.Version, w http.Re

var errStream io.Writer

fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")

if len(r.Header.Get("Upgrade")) > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know how we should deal with an empty, yet provided "Upgrade" header field. Anyway, I think a more idiomatic way of checking, which doesn't involve testing the field's value length would be:

 if _, ok := r.Header.Get("Upgrade"); ok {

I'll let you check the spec if syntax isn't clear (also, parenthesis around conditions are not required in Go).

Signed-off-by: Jean-Tiare Le Bigot <jt@yadutaf.fr>
@yadutaf
Copy link
Contributor Author
yadutaf commented Dec 15, 2014

@icecrime thanks for the review. Should be fine now. I also fixed the formating issue.

@SvenDowideit
Copy link
Contributor

mmm, it occurs to me this should be documented in the API docs :)

Signed-off-by: Jean-Tiare Le Bigot <jt@yadutaf.fr>
@yadutaf
Copy link
Contributor Author
yadutaf commented Dec 16, 2014

@SvenDowideit Done.

@SvenDowideit
Copy link
Contributor

sweet - Docs LGTM - @fredlf @jamtur01

The docs will need to be moved to a newly created 1.17 API doc, but until that gets made, this version gives us a good idea of what things look like.

@LK4D4
Copy link
Contributor
LK4D4 commented Dec 17, 2014

LGTM

Sorry, something went wrong.

@icecrime
Copy link
Contributor

Thanks @yadutaf! LGTM (a.k.a: "yapludtaf")

LK4D4 added a commit that referenced this pull request Dec 17, 2014
Set HTTP upgrade headers when hijacking connection
@LK4D4 LK4D4 merged commit f538e4b into moby:master Dec 17, 2014
@SvenDowideit
Copy link
Contributor

NOTE to sven&fred: please do not merge this into a docs update for 1.4.1 - the API doc needed to be in the 1.17 API doc.

@SvenDowideit
Copy link
Contributor

@jfrazelle @LK4D4 this needs to be fixed up in master - as this change is not in 1.4, it should not be documented in the 1.16 API docs.

@icecrime icecrime added this to the 1.5.0 milestone Dec 19, 2014
icecrime pushed a commit to icecrime/docker that referenced this pull request Dec 19, 2014
Because the patch is not in 1.4, the v1.16 docs shouldn't have been
updated. Docs were promoted to v1.17 by moby#9742.

Signed-off-by: Arnaud Porterie <arnaud.porterie@docker.com>
jessfraz pushed a commit that referenced this pull request Dec 22, 2014
@@ -887,7 +887,11 @@ func postContainersAttach(eng *engine.Engine, version version.Version, w http.Re

var errStream io.Writer

fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
if _, ok := r.Header["Upgrade"]; ok {
fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of hardcoding tcp here should we look at the Upgrade value in the env ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would not make sense. Let's say you have a client telling you "I want websocket" protocol. Then you answer "Sure, here is websocket protocol" while actually sending raw tcp, I fear some debugging nightmare.

If need be, I'd rather add a check on the incoming UPGRADE header to make sure this is something we can speak.

The reason why I did not bother doing it is that this is barely a hint for potential proxies. Docker just ignores it (I mean, It won't affect its behavior in any way beyond sending headers) and Nginx at least will just plainly ignore the value. Actually, If the value had been important here, I'd have used a standard, well known one instead.

Opinion ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, it makes sense for me to leave it like this. Thanks.

ColinHuang pushed a commit to fcwu/docker that referenced this pull request Jan 5, 2015
Because the patch is not in 1.4, the v1.16 docs shouldn't have been
updated. Docs were promoted to v1.17 by moby#9742.

Signed-off-by: Arnaud Porterie <arnaud.porterie@docker.com>
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

Successfully merging this pull request may close these issues.

7 participants
0