Merge pull request #62 from EmileVauge/websockets-support
Websockets support
This commit is contained in:
commit
c452fd2195
5 changed files with 306 additions and 89 deletions
17
README.md
17
README.md
|
@ -25,7 +25,8 @@ It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/
|
||||||
- Tiny docker image included
|
- Tiny docker image included
|
||||||
- SSL backends support
|
- SSL backends support
|
||||||
- SSL frontend support
|
- SSL frontend support
|
||||||
- WebUI
|
- Clean AngularJS Web UI
|
||||||
|
- Websocket support
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
|
@ -33,6 +34,13 @@ Here is a demo of Træfɪk using Docker backend, showing a load-balancing betwee
|
||||||
|
|
||||||
[![asciicast](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko.png)](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko)
|
[![asciicast](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko.png)](https://asciinema.org/a/4tcyde7riou5vxulo6my3mtko)
|
||||||
|
|
||||||
|
## Web UI
|
||||||
|
|
||||||
|
You can access to a simple HTML frontend of Træfik.
|
||||||
|
|
||||||
|
![Web UI Providers](docs/img/web.frontend.png)
|
||||||
|
![Web UI Health](docs/img/traefik-health.png)
|
||||||
|
|
||||||
## Plumbing
|
## Plumbing
|
||||||
|
|
||||||
- [Oxy](https://github.com/mailgun/oxy/): an awsome proxy library made by Mailgun guys
|
- [Oxy](https://github.com/mailgun/oxy/): an awsome proxy library made by Mailgun guys
|
||||||
|
@ -68,13 +76,6 @@ You can find the complete documentation [here](docs/index.md).
|
||||||
|
|
||||||
Refer to the [benchmarks section](docs/index.md#benchmarks) in the documentation.
|
Refer to the [benchmarks section](docs/index.md#benchmarks) in the documentation.
|
||||||
|
|
||||||
## Web UI
|
|
||||||
|
|
||||||
You can access to a simple HTML frontend of Træfik.
|
|
||||||
|
|
||||||
![Web UI Providers](docs/img/web.frontend.png)
|
|
||||||
![Web UI Health](docs/img/traefik-health.png)
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
149
glide.yaml
149
glide.yaml
|
@ -1,81 +1,11 @@
|
||||||
package: main
|
package: main
|
||||||
import:
|
import:
|
||||||
- package: github.com/mailgun/timetools
|
|
||||||
ref: fd192d755b00c968d312d23f521eb0cdc6f66bd0
|
|
||||||
- package: github.com/coreos/go-etcd
|
- package: github.com/coreos/go-etcd
|
||||||
ref: cc90c7b091275e606ad0ca7102a23fb2072f3f5e
|
ref: cc90c7b091275e606ad0ca7102a23fb2072f3f5e
|
||||||
subpackages:
|
subpackages:
|
||||||
- etcd
|
- etcd
|
||||||
- package: github.com/davecgh/go-spew
|
|
||||||
ref: 2df174808ee097f90d259e432cc04442cf60be21
|
|
||||||
subpackages:
|
|
||||||
- spew
|
|
||||||
- package: gopkg.in/fsnotify.v1
|
|
||||||
ref: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0
|
|
||||||
- package: github.com/BurntSushi/ty
|
|
||||||
ref: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74
|
|
||||||
- package: github.com/hashicorp/consul
|
|
||||||
ref: de080672fee9e6104572eeea89eccdca135bb918
|
|
||||||
subpackages:
|
|
||||||
- api
|
|
||||||
- package: github.com/alecthomas/template
|
|
||||||
ref: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0
|
|
||||||
- package: github.com/thoas/stats
|
|
||||||
ref: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8
|
|
||||||
- package: github.com/vdemeester/shakers
|
|
||||||
ref: 8fe734f75f3a70b651cbfbf8a55a009da09e8dc5
|
|
||||||
- package: github.com/samuel/go-zookeeper
|
|
||||||
ref: fa6674abf3f4580b946a01bf7a1ce4ba8766205b
|
|
||||||
subpackages:
|
|
||||||
- zk
|
|
||||||
- package: github.com/alecthomas/units
|
|
||||||
ref: 6b4e7dc5e3143b85ea77909c72caf89416fc2915
|
|
||||||
- package: github.com/unrolled/render
|
|
||||||
ref: 26b4e3aac686940fe29521545afad9966ddfc80c
|
|
||||||
- package: github.com/flynn/go-shlex
|
|
||||||
ref: 3f9db97f856818214da2e1057f8ad84803971cff
|
|
||||||
- package: github.com/fsouza/go-dockerclient
|
|
||||||
ref: 0239034d42f665efa17fd77c39f891c2f9f32922
|
|
||||||
- package: github.com/codegangsta/negroni
|
|
||||||
ref: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b
|
|
||||||
- package: gopkg.in/yaml.v2
|
|
||||||
ref: 7ad95dd0798a40da1ccdff6dff35fd177b5edf40
|
|
||||||
- package: github.com/opencontainers/runc
|
|
||||||
ref: 4ab132458fc3e9dbeea624153e0331952dc4c8d5
|
|
||||||
subpackages:
|
|
||||||
- libcontainer/user
|
|
||||||
- package: github.com/boltdb/bolt
|
|
||||||
ref: 51f99c862475898df9773747d3accd05a7ca33c1
|
|
||||||
- package: github.com/docker/libtrust
|
|
||||||
ref: 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
|
||||||
- package: github.com/elazarl/go-bindata-assetfs
|
|
||||||
ref: d5cac425555ca5cf00694df246e04f05e6a55150
|
|
||||||
- package: github.com/docker/distribution
|
- package: github.com/docker/distribution
|
||||||
ref: 9038e48c3b982f8e82281ea486f078a73731ac4e
|
ref: 9038e48c3b982f8e82281ea486f078a73731ac4e
|
||||||
- package: github.com/BurntSushi/toml
|
|
||||||
ref: bd2bdf7f18f849530ef7a1c29a4290217cab32a1
|
|
||||||
- package: github.com/samalba/dockerclient
|
|
||||||
ref: cfb489c624b635251a93e74e1e90eb0959c5367f
|
|
||||||
- package: gopkg.in/check.v1
|
|
||||||
ref: 11d3bc7aa68e238947792f30573146a3231fc0f1
|
|
||||||
- package: gopkg.in/alecthomas/kingpin.v2
|
|
||||||
ref: 639879d6110b1b0409410c7b737ef0bb18325038
|
|
||||||
- package: github.com/Sirupsen/logrus
|
|
||||||
ref: 418b41d23a1bf978c06faea5313ba194650ac088
|
|
||||||
- package: golang.org/x/net
|
|
||||||
ref: d9558e5c97f85372afee28cf2b6059d7d3818919
|
|
||||||
subpackages:
|
|
||||||
- context
|
|
||||||
- package: gopkg.in/mgo.v2
|
|
||||||
ref: 22287bab4379e1fbf6002fb4eb769888f3fb224c
|
|
||||||
subpackages:
|
|
||||||
- bson
|
|
||||||
- package: github.com/gambol99/go-marathon
|
|
||||||
ref: 0ba31bcb0d7633ba1888d744c42990eb15281cf1
|
|
||||||
- package: github.com/mailgun/manners
|
|
||||||
ref: 37136f736785d7c6aa3b9a27b4b2dd1028ca6d79
|
|
||||||
- package: github.com/gorilla/handlers
|
|
||||||
ref: 40694b40f4a928c062f56849989d3e9cd0570e5f
|
|
||||||
- package: github.com/mailgun/log
|
- package: github.com/mailgun/log
|
||||||
ref: 44874009257d4d47ba9806f1b7f72a32a015e4d8
|
ref: 44874009257d4d47ba9806f1b7f72a32a015e4d8
|
||||||
- package: github.com/mailgun/oxy
|
- package: github.com/mailgun/oxy
|
||||||
|
@ -86,6 +16,54 @@ import:
|
||||||
- memmetrics
|
- memmetrics
|
||||||
- roundrobin
|
- roundrobin
|
||||||
- utils
|
- utils
|
||||||
|
- package: github.com/hashicorp/consul
|
||||||
|
ref: de080672fee9e6104572eeea89eccdca135bb918
|
||||||
|
subpackages:
|
||||||
|
- api
|
||||||
|
- package: github.com/samuel/go-zookeeper
|
||||||
|
ref: fa6674abf3f4580b946a01bf7a1ce4ba8766205b
|
||||||
|
subpackages:
|
||||||
|
- zk
|
||||||
|
- package: github.com/docker/libtrust
|
||||||
|
ref: 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
||||||
|
- package: gopkg.in/check.v1
|
||||||
|
ref: 11d3bc7aa68e238947792f30573146a3231fc0f1
|
||||||
|
- package: golang.org/x/net
|
||||||
|
ref: d9558e5c97f85372afee28cf2b6059d7d3818919
|
||||||
|
subpackages:
|
||||||
|
- context
|
||||||
|
- package: github.com/gorilla/handlers
|
||||||
|
ref: 40694b40f4a928c062f56849989d3e9cd0570e5f
|
||||||
|
- package: github.com/docker/libkv
|
||||||
|
ref: 3732f7ff1b56057c3158f10bceb1e79133025373
|
||||||
|
- package: github.com/alecthomas/template
|
||||||
|
ref: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0
|
||||||
|
- package: github.com/vdemeester/shakers
|
||||||
|
ref: 8fe734f75f3a70b651cbfbf8a55a009da09e8dc5
|
||||||
|
- package: github.com/alecthomas/units
|
||||||
|
ref: 6b4e7dc5e3143b85ea77909c72caf89416fc2915
|
||||||
|
- package: github.com/gambol99/go-marathon
|
||||||
|
ref: 0ba31bcb0d7633ba1888d744c42990eb15281cf1
|
||||||
|
- package: github.com/mailgun/predicate
|
||||||
|
ref: cb0bff91a7ab7cf7571e661ff883fc997bc554a3
|
||||||
|
- package: github.com/thoas/stats
|
||||||
|
ref: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8
|
||||||
|
- package: github.com/samalba/dockerclient
|
||||||
|
ref: cfb489c624b635251a93e74e1e90eb0959c5367f
|
||||||
|
- package: github.com/Sirupsen/logrus
|
||||||
|
ref: 418b41d23a1bf978c06faea5313ba194650ac088
|
||||||
|
- package: github.com/unrolled/render
|
||||||
|
ref: 26b4e3aac686940fe29521545afad9966ddfc80c
|
||||||
|
- package: github.com/flynn/go-shlex
|
||||||
|
ref: 3f9db97f856818214da2e1057f8ad84803971cff
|
||||||
|
- package: github.com/fsouza/go-dockerclient
|
||||||
|
ref: 0239034d42f665efa17fd77c39f891c2f9f32922
|
||||||
|
- package: github.com/boltdb/bolt
|
||||||
|
ref: 51f99c862475898df9773747d3accd05a7ca33c1
|
||||||
|
- package: gopkg.in/mgo.v2
|
||||||
|
ref: 22287bab4379e1fbf6002fb4eb769888f3fb224c
|
||||||
|
subpackages:
|
||||||
|
- bson
|
||||||
- package: github.com/docker/docker
|
- package: github.com/docker/docker
|
||||||
ref: f39987afe8d611407887b3094c03d6ba6a766a67
|
ref: f39987afe8d611407887b3094c03d6ba6a766a67
|
||||||
subpackages:
|
subpackages:
|
||||||
|
@ -125,6 +103,26 @@ import:
|
||||||
- runconfig
|
- runconfig
|
||||||
- utils
|
- utils
|
||||||
- volume
|
- volume
|
||||||
|
- package: github.com/mailgun/timetools
|
||||||
|
ref: fd192d755b00c968d312d23f521eb0cdc6f66bd0
|
||||||
|
- package: github.com/codegangsta/negroni
|
||||||
|
ref: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b
|
||||||
|
- package: gopkg.in/yaml.v2
|
||||||
|
ref: 7ad95dd0798a40da1ccdff6dff35fd177b5edf40
|
||||||
|
- package: github.com/opencontainers/runc
|
||||||
|
ref: 4ab132458fc3e9dbeea624153e0331952dc4c8d5
|
||||||
|
subpackages:
|
||||||
|
- libcontainer/user
|
||||||
|
- package: github.com/gorilla/mux
|
||||||
|
ref: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a
|
||||||
|
- package: github.com/BurntSushi/ty
|
||||||
|
ref: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74
|
||||||
|
- package: github.com/elazarl/go-bindata-assetfs
|
||||||
|
ref: d5cac425555ca5cf00694df246e04f05e6a55150
|
||||||
|
- package: github.com/BurntSushi/toml
|
||||||
|
ref: bd2bdf7f18f849530ef7a1c29a4290217cab32a1
|
||||||
|
- package: gopkg.in/alecthomas/kingpin.v2
|
||||||
|
ref: 639879d6110b1b0409410c7b737ef0bb18325038
|
||||||
- package: github.com/docker/libcompose
|
- package: github.com/docker/libcompose
|
||||||
ref: 79ef5d150f053a5b12f16b02d8844ed7cf33611a
|
ref: 79ef5d150f053a5b12f16b02d8844ed7cf33611a
|
||||||
subpackages:
|
subpackages:
|
||||||
|
@ -135,13 +133,12 @@ import:
|
||||||
- utils
|
- utils
|
||||||
- package: github.com/cenkalti/backoff
|
- package: github.com/cenkalti/backoff
|
||||||
ref: 4dc77674aceaabba2c7e3da25d4c823edfb73f99
|
ref: 4dc77674aceaabba2c7e3da25d4c823edfb73f99
|
||||||
|
- package: gopkg.in/fsnotify.v1
|
||||||
|
ref: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0
|
||||||
|
- package: github.com/mailgun/manners
|
||||||
|
ref: 37136f736785d7c6aa3b9a27b4b2dd1028ca6d79
|
||||||
- package: github.com/gorilla/context
|
- package: github.com/gorilla/context
|
||||||
ref: 215affda49addc4c8ef7e2534915df2c8c35c6cd
|
ref: 215affda49addc4c8ef7e2534915df2c8c35c6cd
|
||||||
- package: github.com/docker/libkv
|
|
||||||
ref: 3732f7ff1b56057c3158f10bceb1e79133025373
|
|
||||||
- package: github.com/codahale/hdrhistogram
|
- package: github.com/codahale/hdrhistogram
|
||||||
ref: 954f16e8b9ef0e5d5189456aa4c1202758e04f17
|
ref: 954f16e8b9ef0e5d5189456aa4c1202758e04f17
|
||||||
- package: github.com/mailgun/predicate
|
- package: github.com/gorilla/websocket
|
||||||
ref: cb0bff91a7ab7cf7571e661ff883fc997bc554a3
|
|
||||||
- package: github.com/gorilla/mux
|
|
||||||
ref: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a
|
|
||||||
|
|
52
middlewares/websocket.go
Normal file
52
middlewares/websocket.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
Copyright
|
||||||
|
*/
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/mailgun/oxy/roundrobin"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WebsocketUpgrader struct {
|
||||||
|
rr *roundrobin.RoundRobin
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebsocketUpgrader(rr *roundrobin.RoundRobin) *WebsocketUpgrader {
|
||||||
|
wu := WebsocketUpgrader{
|
||||||
|
rr: rr,
|
||||||
|
}
|
||||||
|
return &wu
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *WebsocketUpgrader) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
// If request is websocket, serve with golang websocket server to do protocol handshake
|
||||||
|
if strings.Join(req.Header["Upgrade"], "") == "websocket" {
|
||||||
|
start := time.Now().UTC()
|
||||||
|
url, err := u.rr.NextServer()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Can't round robin in websocket middleware")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("Websocket forward to %s", url.String())
|
||||||
|
NewProxy(url).ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if req.TLS != nil {
|
||||||
|
log.Debugf("Round trip: %v, duration: %v tls:version: %x, tls:resume:%t, tls:csuite:%x, tls:server:%v",
|
||||||
|
req.URL, time.Now().UTC().Sub(start),
|
||||||
|
req.TLS.Version,
|
||||||
|
req.TLS.DidResume,
|
||||||
|
req.TLS.CipherSuite,
|
||||||
|
req.TLS.ServerName)
|
||||||
|
} else {
|
||||||
|
log.Debugf("Round trip: %v, duration: %v",
|
||||||
|
req.URL, time.Now().UTC().Sub(start))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u.rr.ServeHTTP(w, req)
|
||||||
|
}
|
170
middlewares/websocketproxy.go
Normal file
170
middlewares/websocketproxy.go
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Original developpement made by https://github.com/koding/websocketproxy
|
||||||
|
var (
|
||||||
|
// DefaultUpgrader specifies the parameters for upgrading an HTTP
|
||||||
|
// connection to a WebSocket connection.
|
||||||
|
DefaultUpgrader = &websocket.Upgrader{
|
||||||
|
ReadBufferSize: 1024,
|
||||||
|
WriteBufferSize: 1024,
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultDialer is a dialer with all fields set to the default zero values.
|
||||||
|
DefaultDialer = websocket.DefaultDialer
|
||||||
|
)
|
||||||
|
|
||||||
|
// WebsocketProxy is an HTTP Handler that takes an incoming WebSocket
|
||||||
|
// connection and proxies it to another server.
|
||||||
|
type WebsocketProxy struct {
|
||||||
|
// Backend returns the backend URL which the proxy uses to reverse proxy
|
||||||
|
// the incoming WebSocket connection. Request is the initial incoming and
|
||||||
|
// unmodified request.
|
||||||
|
Backend func(*http.Request) *url.URL
|
||||||
|
|
||||||
|
// Upgrader specifies the parameters for upgrading a incoming HTTP
|
||||||
|
// connection to a WebSocket connection. If nil, DefaultUpgrader is used.
|
||||||
|
Upgrader *websocket.Upgrader
|
||||||
|
|
||||||
|
// Dialer contains options for connecting to the backend WebSocket server.
|
||||||
|
// If nil, DefaultDialer is used.
|
||||||
|
Dialer *websocket.Dialer
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProxyHandler returns a new http.Handler interface that reverse proxies the
|
||||||
|
// request to the given target.
|
||||||
|
func ProxyHandler(target *url.URL) http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
NewProxy(target).ServeHTTP(rw, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProxy returns a new Websocket reverse proxy that rewrites the
|
||||||
|
// URL's to the scheme, host and base path provider in target.
|
||||||
|
func NewProxy(target *url.URL) *WebsocketProxy {
|
||||||
|
backend := func(r *http.Request) *url.URL {
|
||||||
|
// Shallow copy
|
||||||
|
u := *target
|
||||||
|
u.Fragment = r.URL.Fragment
|
||||||
|
u.Path = r.URL.Path
|
||||||
|
u.RawQuery = r.URL.RawQuery
|
||||||
|
rurl := u.String()
|
||||||
|
if strings.HasPrefix(rurl, "http") {
|
||||||
|
u.Scheme = "ws"
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(rurl, "https") {
|
||||||
|
u.Scheme = "wss"
|
||||||
|
}
|
||||||
|
return &u
|
||||||
|
}
|
||||||
|
return &WebsocketProxy{Backend: backend}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP implements the http.Handler that proxies WebSocket connections.
|
||||||
|
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if w.Backend == nil {
|
||||||
|
log.Println("websocketproxy: backend function is not defined")
|
||||||
|
http.Error(rw, "internal server error (code: 1)", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backendURL := w.Backend(req)
|
||||||
|
if backendURL == nil {
|
||||||
|
log.Println("websocketproxy: backend URL is nil")
|
||||||
|
http.Error(rw, "internal server error (code: 2)", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer := w.Dialer
|
||||||
|
if w.Dialer == nil {
|
||||||
|
dialer = DefaultDialer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass headers from the incoming request to the dialer to forward them to
|
||||||
|
// the final destinations.
|
||||||
|
requestHeader := http.Header{}
|
||||||
|
requestHeader.Add("Origin", req.Header.Get("Origin"))
|
||||||
|
for _, prot := range req.Header[http.CanonicalHeaderKey("Sec-WebSocket-Protocol")] {
|
||||||
|
requestHeader.Add("Sec-WebSocket-Protocol", prot)
|
||||||
|
}
|
||||||
|
for _, cookie := range req.Header[http.CanonicalHeaderKey("Cookie")] {
|
||||||
|
requestHeader.Add("Cookie", cookie)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass X-Forwarded-For headers too, code below is a part of
|
||||||
|
// httputil.ReverseProxy. See http://en.wikipedia.org/wiki/X-Forwarded-For
|
||||||
|
// for more information
|
||||||
|
// TODO: use RFC7239 http://tools.ietf.org/html/rfc7239
|
||||||
|
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
||||||
|
// If we aren't the first proxy retain prior
|
||||||
|
// X-Forwarded-For information as a comma+space
|
||||||
|
// separated list and fold multiple headers into one.
|
||||||
|
if prior, ok := req.Header["X-Forwarded-For"]; ok {
|
||||||
|
clientIP = strings.Join(prior, ", ") + ", " + clientIP
|
||||||
|
}
|
||||||
|
requestHeader.Set("X-Forwarded-For", clientIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the originating protocol of the incoming HTTP request. The SSL might
|
||||||
|
// be terminated on our site and because we doing proxy adding this would
|
||||||
|
// be helpful for applications on the backend.
|
||||||
|
requestHeader.Set("X-Forwarded-Proto", "http")
|
||||||
|
if req.TLS != nil {
|
||||||
|
requestHeader.Set("X-Forwarded-Proto", "https")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to the backend URL, also pass the headers we get from the requst
|
||||||
|
// together with the Forwarded headers we prepared above.
|
||||||
|
// TODO: support multiplexing on the same backend connection instead of
|
||||||
|
// opening a new TCP connection time for each request. This should be
|
||||||
|
// optional:
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-01
|
||||||
|
connBackend, resp, err := dialer.Dial(backendURL.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("websocketproxy: couldn't dial to remote backend url %s, %s, %+v", backendURL.String(), err, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer connBackend.Close()
|
||||||
|
|
||||||
|
upgrader := w.Upgrader
|
||||||
|
if w.Upgrader == nil {
|
||||||
|
upgrader = DefaultUpgrader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only pass those headers to the upgrader.
|
||||||
|
upgradeHeader := http.Header{}
|
||||||
|
upgradeHeader.Set("Sec-WebSocket-Protocol",
|
||||||
|
resp.Header.Get(http.CanonicalHeaderKey("Sec-WebSocket-Protocol")))
|
||||||
|
upgradeHeader.Set("Set-Cookie",
|
||||||
|
resp.Header.Get(http.CanonicalHeaderKey("Set-Cookie")))
|
||||||
|
|
||||||
|
// Now upgrade the existing incoming request to a WebSocket connection.
|
||||||
|
// Also pass the header that we gathered from the Dial handshake.
|
||||||
|
connPub, err := upgrader.Upgrade(rw, req, upgradeHeader)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("websocketproxy: couldn't upgrade %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer connPub.Close()
|
||||||
|
|
||||||
|
errc := make(chan error, 2)
|
||||||
|
cp := func(dst io.Writer, src io.Reader) {
|
||||||
|
_, err := io.Copy(dst, src)
|
||||||
|
errc <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start our proxy now, everything is ready...
|
||||||
|
go cp(connBackend.UnderlyingConn(), connPub.UnderlyingConn())
|
||||||
|
go cp(connPub.UnderlyingConn(), connBackend.UnderlyingConn())
|
||||||
|
<-errc
|
||||||
|
}
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/negroni"
|
"github.com/codegangsta/negroni"
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/emilevauge/traefik/middlewares"
|
"github.com/emilevauge/traefik/middlewares"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/mailgun/manners"
|
"github.com/mailgun/manners"
|
||||||
|
@ -88,7 +87,7 @@ func main() {
|
||||||
} else {
|
} else {
|
||||||
log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true})
|
log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true})
|
||||||
}
|
}
|
||||||
log.Debugf("Global configuration loaded %s", spew.Sdump(globalConfiguration))
|
log.Debugf("Global configuration loaded %+v", globalConfiguration)
|
||||||
configurationRouter = LoadDefaultConfig(globalConfiguration)
|
configurationRouter = LoadDefaultConfig(globalConfiguration)
|
||||||
|
|
||||||
// listen new configurations from providers
|
// listen new configurations from providers
|
||||||
|
@ -97,7 +96,6 @@ func main() {
|
||||||
for {
|
for {
|
||||||
configMsg := <-configurationChan
|
configMsg := <-configurationChan
|
||||||
log.Infof("Configuration receveived from provider %s: %#v", configMsg.providerName, configMsg.configuration)
|
log.Infof("Configuration receveived from provider %s: %#v", configMsg.providerName, configMsg.configuration)
|
||||||
log.Debugf("Configuration %s", spew.Sdump(configMsg.configuration))
|
|
||||||
if configMsg.configuration == nil {
|
if configMsg.configuration == nil {
|
||||||
log.Info("Skipping empty configuration")
|
log.Info("Skipping empty configuration")
|
||||||
} else if reflect.DeepEqual(currentConfigurations[configMsg.providerName], configMsg.configuration) {
|
} else if reflect.DeepEqual(currentConfigurations[configMsg.providerName], configMsg.configuration) {
|
||||||
|
@ -191,7 +189,6 @@ func main() {
|
||||||
|
|
||||||
func startServer(srv *manners.GracefulServer, globalConfiguration *GlobalConfiguration) {
|
func startServer(srv *manners.GracefulServer, globalConfiguration *GlobalConfiguration) {
|
||||||
log.Info("Starting server")
|
log.Info("Starting server")
|
||||||
log.Debugf("Server %s", spew.Sdump(srv))
|
|
||||||
if len(globalConfiguration.CertFile) > 0 && len(globalConfiguration.KeyFile) > 0 {
|
if len(globalConfiguration.CertFile) > 0 && len(globalConfiguration.KeyFile) > 0 {
|
||||||
err := srv.ListenAndServeTLS(globalConfiguration.CertFile, globalConfiguration.KeyFile)
|
err := srv.ListenAndServeTLS(globalConfiguration.CertFile, globalConfiguration.KeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -281,7 +278,7 @@ func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration
|
||||||
}
|
}
|
||||||
case wrr:
|
case wrr:
|
||||||
log.Infof("Creating load-balancer wrr")
|
log.Infof("Creating load-balancer wrr")
|
||||||
lb = rr
|
lb = middlewares.NewWebsocketUpgrader(rr)
|
||||||
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
||||||
url, err := url.Parse(server.URL)
|
url, err := url.Parse(server.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue