Merge v1.4.1 into master
This commit is contained in:
commit
a0c72cdf00
24 changed files with 531 additions and 113 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,5 +1,16 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## [v1.4.1](https://github.com/containous/traefik/tree/v1.4.1) (2017-10-24)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.0...v1.4.1)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[docker]** Network filter ([#2301](https://github.com/containous/traefik/pull/2301) by [ldez](https://github.com/ldez))
|
||||||
|
- **[healthcheck]** Fix healthcheck path ([#2295](https://github.com/containous/traefik/pull/2295) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- **[rules]** Regex capturing group. ([#2296](https://github.com/containous/traefik/pull/2296) by [ldez](https://github.com/ldez))
|
||||||
|
- **[websocket]** Force http/1.1 for websocket ([#2292](https://github.com/containous/traefik/pull/2292) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Stream mode when http2 ([#2309](https://github.com/containous/traefik/pull/2309) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Enhance Trust Forwarded Headers ([#2302](https://github.com/containous/traefik/pull/2302) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
## [v1.4.0](https://github.com/containous/traefik/tree/v1.4.0) (2017-10-16)
|
## [v1.4.0](https://github.com/containous/traefik/tree/v1.4.0) (2017-10-16)
|
||||||
[All Commits](https://github.com/containous/traefik/compare/v1.3.0-rc1...v1.4.0)
|
[All Commits](https://github.com/containous/traefik/compare/v1.3.0-rc1...v1.4.0)
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,8 @@ Complete documentation is available at https://traefik.io`,
|
||||||
Config: traefikConfiguration,
|
Config: traefikConfiguration,
|
||||||
DefaultPointersConfig: traefikPointersConfiguration,
|
DefaultPointersConfig: traefikPointersConfiguration,
|
||||||
Run: func() error {
|
Run: func() error {
|
||||||
|
traefikConfiguration.GlobalConfiguration.SetEffectiveConfiguration(traefikConfiguration.ConfigFile)
|
||||||
|
|
||||||
if traefikConfiguration.Web == nil {
|
if traefikConfiguration.Web == nil {
|
||||||
fmt.Println("Please enable the web provider to use healtcheck.")
|
fmt.Println("Please enable the web provider to use healtcheck.")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -121,7 +123,8 @@ Complete documentation is available at https://traefik.io`,
|
||||||
}
|
}
|
||||||
client.Transport = tr
|
client.Transport = tr
|
||||||
}
|
}
|
||||||
resp, err := client.Head(protocol + "://" + traefikConfiguration.Web.Address + "/ping")
|
|
||||||
|
resp, err := client.Head(protocol + "://" + traefikConfiguration.Web.Address + traefikConfiguration.Web.Path + "ping")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error calling healthcheck: %s\n", err)
|
fmt.Printf("Error calling healthcheck: %s\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -138,6 +138,10 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if gc.Web != nil && (gc.Web.Path == "" || !strings.HasSuffix(gc.Web.Path, "/")) {
|
||||||
|
gc.Web.Path += "/"
|
||||||
|
}
|
||||||
|
|
||||||
// Try to fallback to traefik config file in case the file provider is enabled
|
// Try to fallback to traefik config file in case the file provider is enabled
|
||||||
// but has no file name configured.
|
// but has no file name configured.
|
||||||
if gc.File != nil && len(gc.File.Filename) == 0 {
|
if gc.File != nil && len(gc.File.Filename) == 0 {
|
||||||
|
|
8
glide.lock
generated
8
glide.lock
generated
|
@ -1,5 +1,5 @@
|
||||||
hash: b929df3c022d8a67b8d174f81257a502670c3683e801f8a53283c2965c921c6e
|
hash: 90d53da9a6eaba85d524d95410051ff7f12b1f76181df2ad4796b2439f3a2a37
|
||||||
updated: 2017-10-16T23:09:16.848940186+02:00
|
updated: 2017-10-24T14:08:11.364720581+02:00
|
||||||
imports:
|
imports:
|
||||||
- name: cloud.google.com/go
|
- name: cloud.google.com/go
|
||||||
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
||||||
|
@ -89,7 +89,7 @@ imports:
|
||||||
- name: github.com/containous/flaeg
|
- name: github.com/containous/flaeg
|
||||||
version: b5d2dc5878df07c2d74413348186982e7b865871
|
version: b5d2dc5878df07c2d74413348186982e7b865871
|
||||||
- name: github.com/containous/mux
|
- name: github.com/containous/mux
|
||||||
version: af6ea922f7683d9706834157e6b0610e22ccb2db
|
version: 06ccd3e75091eb659b1d720cda0e16bc7057954c
|
||||||
- name: github.com/containous/staert
|
- name: github.com/containous/staert
|
||||||
version: 1e26a71803e428fd933f5f9c8e50a26878f53147
|
version: 1e26a71803e428fd933f5f9c8e50a26878f53147
|
||||||
- name: github.com/coreos/etcd
|
- name: github.com/coreos/etcd
|
||||||
|
@ -485,7 +485,7 @@ imports:
|
||||||
- name: github.com/urfave/negroni
|
- name: github.com/urfave/negroni
|
||||||
version: 490e6a555d47ca891a89a150d0c1ef3922dfffe9
|
version: 490e6a555d47ca891a89a150d0c1ef3922dfffe9
|
||||||
- name: github.com/vulcand/oxy
|
- name: github.com/vulcand/oxy
|
||||||
version: c024a22700b56debed9a9c8dbb297210a7ece02d
|
version: 7e9763c4dc71b9758379da3581e6495c145caaab
|
||||||
repo: https://github.com/containous/oxy.git
|
repo: https://github.com/containous/oxy.git
|
||||||
vcs: git
|
vcs: git
|
||||||
subpackages:
|
subpackages:
|
||||||
|
|
|
@ -12,7 +12,7 @@ import:
|
||||||
- package: github.com/cenk/backoff
|
- package: github.com/cenk/backoff
|
||||||
- package: github.com/containous/flaeg
|
- package: github.com/containous/flaeg
|
||||||
- package: github.com/vulcand/oxy
|
- package: github.com/vulcand/oxy
|
||||||
version: c024a22700b56debed9a9c8dbb297210a7ece02d
|
version: 7e9763c4dc71b9758379da3581e6495c145caaab
|
||||||
repo: https://github.com/containous/oxy.git
|
repo: https://github.com/containous/oxy.git
|
||||||
vcs: git
|
vcs: git
|
||||||
subpackages:
|
subpackages:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defaultEntryPoints = ["wss"]
|
defaultEntryPoints = ["wss"]
|
||||||
|
|
||||||
logLevel = "DEBUG"
|
logLevel = "DEBUG"
|
||||||
|
InsecureSkipVerify=true
|
||||||
|
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.wss]
|
[entryPoints.wss]
|
||||||
|
@ -24,4 +25,4 @@ logLevel = "DEBUG"
|
||||||
[frontends.frontend1]
|
[frontends.frontend1]
|
||||||
backend = "backend1"
|
backend = "backend1"
|
||||||
[frontends.frontend1.routes.test_1]
|
[frontends.frontend1.routes.test_1]
|
||||||
rule = "Path:/ws"
|
rule = "Path:/echo,/ws"
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -22,7 +24,9 @@ var LocalhostKey []byte
|
||||||
// GRPCSuite
|
// GRPCSuite
|
||||||
type GRPCSuite struct{ BaseSuite }
|
type GRPCSuite struct{ BaseSuite }
|
||||||
|
|
||||||
type myserver struct{}
|
type myserver struct {
|
||||||
|
stopStreamExample chan bool
|
||||||
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) SetUpSuite(c *check.C) {
|
func (s *GRPCSuite) SetUpSuite(c *check.C) {
|
||||||
var err error
|
var err error
|
||||||
|
@ -36,7 +40,15 @@ func (s *myserver) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*
|
||||||
return &helloworld.HelloReply{Message: "Hello " + in.Name}, nil
|
return &helloworld.HelloReply{Message: "Hello " + in.Name}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startGRPCServer(lis net.Listener) error {
|
func (s *myserver) StreamExample(in *helloworld.StreamExampleRequest, server helloworld.Greeter_StreamExampleServer) error {
|
||||||
|
data := make([]byte, 512)
|
||||||
|
rand.Read(data)
|
||||||
|
server.Send(&helloworld.StreamExampleReply{Data: string(data)})
|
||||||
|
<-s.stopStreamExample
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startGRPCServer(lis net.Listener, server *myserver) error {
|
||||||
cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)
|
cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -45,26 +57,30 @@ func startGRPCServer(lis net.Listener) error {
|
||||||
creds := credentials.NewServerTLSFromCert(&cert)
|
creds := credentials.NewServerTLSFromCert(&cert)
|
||||||
serverOption := grpc.Creds(creds)
|
serverOption := grpc.Creds(creds)
|
||||||
|
|
||||||
var s *grpc.Server = grpc.NewServer(serverOption)
|
s := grpc.NewServer(serverOption)
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
helloworld.RegisterGreeterServer(s, &myserver{})
|
helloworld.RegisterGreeterServer(s, server)
|
||||||
return s.Serve(lis)
|
return s.Serve(lis)
|
||||||
}
|
}
|
||||||
|
func getHelloClientGRPC() (helloworld.GreeterClient, func() error, error) {
|
||||||
func callHelloClientGRPC() (string, error) {
|
|
||||||
roots := x509.NewCertPool()
|
roots := x509.NewCertPool()
|
||||||
roots.AppendCertsFromPEM(LocalhostCert)
|
roots.AppendCertsFromPEM(LocalhostCert)
|
||||||
credsClient := credentials.NewClientTLSFromCert(roots, "")
|
credsClient := credentials.NewClientTLSFromCert(roots, "")
|
||||||
conn, err := grpc.Dial("127.0.0.1:4443", grpc.WithTransportCredentials(credsClient))
|
conn, err := grpc.Dial("127.0.0.1:4443", grpc.WithTransportCredentials(credsClient))
|
||||||
|
if err != nil {
|
||||||
|
return nil, func() error { return nil }, err
|
||||||
|
}
|
||||||
|
return helloworld.NewGreeterClient(conn), conn.Close, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func callHelloClientGRPC(name string) (string, error) {
|
||||||
|
client, closer, err := getHelloClientGRPC()
|
||||||
|
defer closer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer conn.Close()
|
|
||||||
client := helloworld.NewGreeterClient(conn)
|
|
||||||
|
|
||||||
name := "World"
|
|
||||||
r, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: name})
|
r, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: name})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -72,13 +88,26 @@ func callHelloClientGRPC() (string, error) {
|
||||||
return r.Message, nil
|
return r.Message, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func callStreamExampleClientGRPC() (helloworld.Greeter_StreamExampleClient, func() error, error) {
|
||||||
|
client, closer, err := getHelloClientGRPC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, closer, err
|
||||||
|
}
|
||||||
|
t, err := client.StreamExample(context.Background(), &helloworld.StreamExampleRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, closer, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, closer, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *GRPCSuite) TestGRPC(c *check.C) {
|
func (s *GRPCSuite) TestGRPC(c *check.C) {
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := startGRPCServer(lis)
|
err := startGRPCServer(lis, &myserver{})
|
||||||
c.Log(err)
|
c.Log(err)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
}()
|
}()
|
||||||
|
@ -106,7 +135,7 @@ func (s *GRPCSuite) TestGRPC(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
var response string
|
var response string
|
||||||
err = try.Do(1*time.Second, func() error {
|
err = try.Do(1*time.Second, func() error {
|
||||||
response, err = callHelloClientGRPC()
|
response, err = callHelloClientGRPC("World")
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -120,7 +149,7 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := startGRPCServer(lis)
|
err := startGRPCServer(lis, &myserver{})
|
||||||
c.Log(err)
|
c.Log(err)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
}()
|
}()
|
||||||
|
@ -148,10 +177,68 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
var response string
|
var response string
|
||||||
err = try.Do(1*time.Second, func() error {
|
err = try.Do(1*time.Second, func() error {
|
||||||
response, err = callHelloClientGRPC()
|
response, err = callHelloClientGRPC("World")
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(response, check.Equals, "Hello World")
|
c.Assert(response, check.Equals, "Hello World")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
|
||||||
|
stopStreamExample := make(chan bool)
|
||||||
|
defer func() { stopStreamExample <- true }()
|
||||||
|
lis, err := net.Listen("tcp", ":0")
|
||||||
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := startGRPCServer(lis, &myserver{
|
||||||
|
stopStreamExample: stopStreamExample,
|
||||||
|
})
|
||||||
|
c.Log(err)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
}()
|
||||||
|
|
||||||
|
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct {
|
||||||
|
CertContent string
|
||||||
|
KeyContent string
|
||||||
|
GRPCServerPort string
|
||||||
|
}{
|
||||||
|
CertContent: string(LocalhostCert),
|
||||||
|
KeyContent: string(LocalhostKey),
|
||||||
|
GRPCServerPort: port,
|
||||||
|
})
|
||||||
|
|
||||||
|
defer os.Remove(file)
|
||||||
|
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||||
|
defer display(c)
|
||||||
|
|
||||||
|
err = cmd.Start()
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
// wait for Traefik
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("Host:127.0.0.1"))
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
var client helloworld.Greeter_StreamExampleClient
|
||||||
|
client, closer, err := callStreamExampleClientGRPC()
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
received := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
tr, _ := client.Recv()
|
||||||
|
c.Assert(len(tr.Data), check.Equals, 512)
|
||||||
|
received <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = try.Do(time.Second*10, func() error {
|
||||||
|
select {
|
||||||
|
case <-received:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.New("failed to receive stream data")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ It is generated from these files:
|
||||||
It has these top-level messages:
|
It has these top-level messages:
|
||||||
HelloRequest
|
HelloRequest
|
||||||
HelloReply
|
HelloReply
|
||||||
|
StreamExampleRequest
|
||||||
|
StreamExampleReply
|
||||||
*/
|
*/
|
||||||
package helloworld
|
package helloworld
|
||||||
|
|
||||||
|
@ -67,9 +69,35 @@ func (m *HelloReply) GetMessage() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StreamExampleRequest struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StreamExampleRequest) Reset() { *m = StreamExampleRequest{} }
|
||||||
|
func (m *StreamExampleRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*StreamExampleRequest) ProtoMessage() {}
|
||||||
|
func (*StreamExampleRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||||
|
|
||||||
|
type StreamExampleReply struct {
|
||||||
|
Data string `protobuf:"bytes,1,opt,name=data" json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StreamExampleReply) Reset() { *m = StreamExampleReply{} }
|
||||||
|
func (m *StreamExampleReply) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*StreamExampleReply) ProtoMessage() {}
|
||||||
|
func (*StreamExampleReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||||
|
|
||||||
|
func (m *StreamExampleReply) GetData() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Data
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest")
|
proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest")
|
||||||
proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply")
|
proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply")
|
||||||
|
proto.RegisterType((*StreamExampleRequest)(nil), "helloworld.StreamExampleRequest")
|
||||||
|
proto.RegisterType((*StreamExampleReply)(nil), "helloworld.StreamExampleReply")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -85,6 +113,8 @@ const _ = grpc.SupportPackageIsVersion4
|
||||||
type GreeterClient interface {
|
type GreeterClient interface {
|
||||||
// Sends a greeting
|
// Sends a greeting
|
||||||
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
|
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
|
||||||
|
// Tick me
|
||||||
|
StreamExample(ctx context.Context, in *StreamExampleRequest, opts ...grpc.CallOption) (Greeter_StreamExampleClient, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type greeterClient struct {
|
type greeterClient struct {
|
||||||
|
@ -104,11 +134,45 @@ func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *greeterClient) StreamExample(ctx context.Context, in *StreamExampleRequest, opts ...grpc.CallOption) (Greeter_StreamExampleClient, error) {
|
||||||
|
stream, err := grpc.NewClientStream(ctx, &_Greeter_serviceDesc.Streams[0], c.cc, "/helloworld.Greeter/StreamExample", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &greeterStreamExampleClient{stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Greeter_StreamExampleClient interface {
|
||||||
|
Recv() (*StreamExampleReply, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type greeterStreamExampleClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *greeterStreamExampleClient) Recv() (*StreamExampleReply, error) {
|
||||||
|
m := new(StreamExampleReply)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Server API for Greeter service
|
// Server API for Greeter service
|
||||||
|
|
||||||
type GreeterServer interface {
|
type GreeterServer interface {
|
||||||
// Sends a greeting
|
// Sends a greeting
|
||||||
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
|
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
|
||||||
|
// Tick me
|
||||||
|
StreamExample(*StreamExampleRequest, Greeter_StreamExampleServer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
|
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
|
||||||
|
@ -133,6 +197,27 @@ func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(in
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _Greeter_StreamExample_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(StreamExampleRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(GreeterServer).StreamExample(m, &greeterStreamExampleServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Greeter_StreamExampleServer interface {
|
||||||
|
Send(*StreamExampleReply) error
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type greeterStreamExampleServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *greeterStreamExampleServer) Send(m *StreamExampleReply) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
var _Greeter_serviceDesc = grpc.ServiceDesc{
|
var _Greeter_serviceDesc = grpc.ServiceDesc{
|
||||||
ServiceName: "helloworld.Greeter",
|
ServiceName: "helloworld.Greeter",
|
||||||
HandlerType: (*GreeterServer)(nil),
|
HandlerType: (*GreeterServer)(nil),
|
||||||
|
@ -142,23 +227,33 @@ var _Greeter_serviceDesc = grpc.ServiceDesc{
|
||||||
Handler: _Greeter_SayHello_Handler,
|
Handler: _Greeter_SayHello_Handler,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "StreamExample",
|
||||||
|
Handler: _Greeter_StreamExample_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
Metadata: "helloworld.proto",
|
Metadata: "helloworld.proto",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("helloworld.proto", fileDescriptor0) }
|
func init() { proto.RegisterFile("helloworld.proto", fileDescriptor0) }
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 175 bytes of a gzipped FileDescriptorProto
|
// 231 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x50, 0xc1, 0x4a, 0xc4, 0x30,
|
||||||
0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88,
|
0x10, 0x35, 0xb0, 0xb8, 0x3a, 0x28, 0xca, 0x20, 0x4b, 0x59, 0x41, 0x96, 0x1c, 0x64, 0x4f, 0xa1,
|
||||||
0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42,
|
0xe8, 0xdd, 0x43, 0x41, 0xf4, 0x58, 0x5a, 0xc4, 0x73, 0xb4, 0x43, 0x15, 0x12, 0x13, 0x93, 0x88,
|
||||||
0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92,
|
0xf6, 0x6f, 0xfc, 0x54, 0x49, 0x6c, 0x31, 0x4a, 0xf1, 0xf6, 0x66, 0xe6, 0xe5, 0xbd, 0x97, 0x07,
|
||||||
0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71,
|
0xc7, 0x4f, 0xa4, 0x94, 0x79, 0x37, 0x4e, 0x75, 0xc2, 0x3a, 0x13, 0x0c, 0xc2, 0xcf, 0x86, 0x73,
|
||||||
0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a,
|
0x38, 0xb8, 0x8d, 0x53, 0x43, 0xaf, 0x6f, 0xe4, 0x03, 0x22, 0x2c, 0x5e, 0xa4, 0xa6, 0x82, 0x6d,
|
||||||
0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64,
|
0xd8, 0x76, 0xbf, 0x49, 0x98, 0x9f, 0x03, 0x8c, 0x1c, 0xab, 0x06, 0x2c, 0x60, 0xa9, 0xc9, 0x7b,
|
||||||
0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x14, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0x19, 0x70, 0x49, 0x67, 0xe6,
|
0xd9, 0x4f, 0xa4, 0x69, 0xe4, 0x2b, 0x38, 0x69, 0x83, 0x23, 0xa9, 0xaf, 0x3f, 0xa4, 0xb6, 0x8a,
|
||||||
0xeb, 0xa5, 0x17, 0x15, 0x24, 0xeb, 0xa5, 0x56, 0x24, 0xe6, 0x16, 0xe4, 0xa4, 0x16, 0x23, 0xa9,
|
0x46, 0x4d, 0xbe, 0x05, 0xfc, 0xb3, 0x8f, 0x3a, 0x08, 0x8b, 0x4e, 0x06, 0x39, 0x39, 0x45, 0x7c,
|
||||||
0x75, 0xe2, 0x07, 0x2b, 0x0e, 0x07, 0xb1, 0x03, 0x40, 0x5e, 0x0a, 0x60, 0x4c, 0x62, 0x03, 0xfb,
|
0xf1, 0xc9, 0x60, 0x79, 0xe3, 0x88, 0x02, 0x39, 0xbc, 0x82, 0xbd, 0x56, 0x0e, 0xc9, 0x18, 0x0b,
|
||||||
0xcd, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xb7, 0xcd, 0xf2, 0xef, 0x00, 0x00, 0x00,
|
0x91, 0x7d, 0x22, 0xcf, 0xbb, 0x5e, 0xcd, 0x5c, 0xac, 0x1a, 0xf8, 0x0e, 0xde, 0xc1, 0xe1, 0x2f,
|
||||||
|
0x57, 0xdc, 0xe4, 0xd4, 0xb9, 0xa0, 0xeb, 0xb3, 0x7f, 0x18, 0x49, 0xb4, 0x64, 0x55, 0x09, 0xa7,
|
||||||
|
0xcf, 0x46, 0xf4, 0xce, 0x3e, 0x0a, 0xfa, 0xbe, 0xf9, 0xec, 0x55, 0x75, 0x94, 0x32, 0xdc, 0x47,
|
||||||
|
0x5c, 0xc7, 0xb2, 0x6b, 0xf6, 0xb0, 0x9b, 0x5a, 0xbf, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x6f,
|
||||||
|
0x5f, 0xae, 0xb8, 0x89, 0x01, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,10 @@ package helloworld;
|
||||||
// The greeting service definition.
|
// The greeting service definition.
|
||||||
service Greeter {
|
service Greeter {
|
||||||
// Sends a greeting
|
// Sends a greeting
|
||||||
rpc SayHello (HelloRequest) returns (HelloReply) {}
|
rpc SayHello (HelloRequest) returns (HelloReply) {};
|
||||||
|
|
||||||
|
rpc StreamExample (StreamExampleRequest) returns (stream StreamExampleReply) {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The request message containing the user's name.
|
// The request message containing the user's name.
|
||||||
|
@ -21,3 +24,9 @@ message HelloRequest {
|
||||||
message HelloReply {
|
message HelloReply {
|
||||||
string message = 1;
|
string message = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message StreamExampleRequest {}
|
||||||
|
|
||||||
|
message StreamExampleReply {
|
||||||
|
string data = 1;
|
||||||
|
}
|
|
@ -441,3 +441,65 @@ func (s *WebsocketSuite) TestURLWithURLEncodedChar(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(string(msg), checker.Equals, "OK")
|
c.Assert(string(msg), checker.Equals, "OK")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *WebsocketSuite) TestSSLhttp2(c *check.C) {
|
||||||
|
var upgrader = gorillawebsocket.Upgrader{} // use default options
|
||||||
|
|
||||||
|
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
for {
|
||||||
|
mt, message, err := c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err = c.WriteMessage(mt, message)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
ts.TLS = &tls.Config{}
|
||||||
|
ts.TLS.NextProtos = append(ts.TLS.NextProtos, `h2`)
|
||||||
|
ts.TLS.NextProtos = append(ts.TLS.NextProtos, `http/1.1`)
|
||||||
|
ts.StartTLS()
|
||||||
|
|
||||||
|
file := s.adaptFile(c, "fixtures/websocket/config_https.toml", struct {
|
||||||
|
WebsocketServer string
|
||||||
|
}{
|
||||||
|
WebsocketServer: ts.URL,
|
||||||
|
})
|
||||||
|
|
||||||
|
defer os.Remove(file)
|
||||||
|
cmd, display := s.traefikCmd(withConfigFile(file), "--debug", "--accesslog")
|
||||||
|
defer display(c)
|
||||||
|
|
||||||
|
err := cmd.Start()
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
// wait for traefik
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
//Add client self-signed cert
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
certContent, err := ioutil.ReadFile("./resources/tls/local.cert")
|
||||||
|
roots.AppendCertsFromPEM(certContent)
|
||||||
|
gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{
|
||||||
|
RootCAs: roots,
|
||||||
|
}
|
||||||
|
conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/echo", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK"))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
_, msg, err := conn.ReadMessage()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(string(msg), checker.Equals, "OK")
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
eventtypes "github.com/docker/docker/api/types/events"
|
eventtypes "github.com/docker/docker/api/types/events"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/docker/go-connections/sockets"
|
"github.com/docker/go-connections/sockets"
|
||||||
|
@ -564,10 +565,10 @@ func (p *Provider) getFrontendRule(container dockerData) string {
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
if labels, err := getLabels(container, []string{labelDockerComposeProject, labelDockerComposeService}); err == nil {
|
if labels, err := getLabels(container, []string{labelDockerComposeProject, labelDockerComposeService}); err == nil {
|
||||||
return "Host:" + p.getSubDomain(labels[labelDockerComposeService]+"."+labels[labelDockerComposeProject]) + "." + p.Domain
|
return "Host:" + getSubDomain(labels[labelDockerComposeService]+"."+labels[labelDockerComposeProject]) + "." + p.Domain
|
||||||
}
|
}
|
||||||
if len(p.Domain) > 0 {
|
if len(p.Domain) > 0 {
|
||||||
return "Host:" + p.getSubDomain(container.ServiceName) + "." + p.Domain
|
return "Host:" + getSubDomain(container.ServiceName) + "." + p.Domain
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -857,7 +858,7 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escape beginning slash "/", convert all others to dash "-", and convert underscores "_" to dash "-"
|
// Escape beginning slash "/", convert all others to dash "-", and convert underscores "_" to dash "-"
|
||||||
func (p *Provider) getSubDomain(name string) string {
|
func getSubDomain(name string) string {
|
||||||
return strings.Replace(strings.Replace(strings.TrimPrefix(name, "/"), "/", "-", -1), "_", "-", -1)
|
return strings.Replace(strings.Replace(strings.TrimPrefix(name, "/"), "/", "-", -1), "_", "-", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -866,8 +867,16 @@ func (p *Provider) listServices(ctx context.Context, dockerClient client.APIClie
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []dockerData{}, err
|
return []dockerData{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serverVersion, err := dockerClient.ServerVersion(ctx)
|
||||||
|
|
||||||
networkListArgs := filters.NewArgs()
|
networkListArgs := filters.NewArgs()
|
||||||
networkListArgs.Add("scope", "swarm")
|
// https://docs.docker.com/engine/api/v1.29/#tag/Network (Docker 17.06)
|
||||||
|
if versions.GreaterThanOrEqualTo(serverVersion.APIVersion, "1.29") {
|
||||||
|
networkListArgs.Add("scope", "swarm")
|
||||||
|
} else {
|
||||||
|
networkListArgs.Add("driver", "overlay")
|
||||||
|
}
|
||||||
|
|
||||||
networkList, err := dockerClient.NetworkList(ctx, dockertypes.NetworkListOptions{Filters: networkListArgs})
|
networkList, err := dockerClient.NetworkList(ctx, dockertypes.NetworkListOptions{Filters: networkListArgs})
|
||||||
|
|
||||||
|
|
|
@ -356,7 +356,7 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
expectedBackends: map[string]*types.Backend{
|
expectedBackends: map[string]*types.Backend{
|
||||||
"backend-foo-service": {
|
"backend-foo-service": {
|
||||||
Servers: map[string]types.Server{
|
Servers: map[string]types.Server{
|
||||||
"service": {
|
"service-0": {
|
||||||
URL: "http://127.0.0.1:2503",
|
URL: "http://127.0.0.1:2503",
|
||||||
Weight: 0,
|
Weight: 0,
|
||||||
},
|
},
|
||||||
|
@ -426,7 +426,7 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
expectedBackends: map[string]*types.Backend{
|
expectedBackends: map[string]*types.Backend{
|
||||||
"backend-foobar": {
|
"backend-foobar": {
|
||||||
Servers: map[string]types.Server{
|
Servers: map[string]types.Server{
|
||||||
"service": {
|
"service-0": {
|
||||||
URL: "https://127.0.0.1:2503",
|
URL: "https://127.0.0.1:2503",
|
||||||
Weight: 80,
|
Weight: 80,
|
||||||
},
|
},
|
||||||
|
@ -435,7 +435,7 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
"backend-test2-anotherservice": {
|
"backend-test2-anotherservice": {
|
||||||
Servers: map[string]types.Server{
|
Servers: map[string]types.Server{
|
||||||
"service": {
|
"service-0": {
|
||||||
URL: "http://127.0.0.1:8079",
|
URL: "http://127.0.0.1:8079",
|
||||||
Weight: 33,
|
Weight: 33,
|
||||||
},
|
},
|
||||||
|
|
|
@ -56,17 +56,9 @@ func goroutines() interface{} {
|
||||||
// Provide allows the provider to provide configurations to traefik
|
// Provide allows the provider to provide configurations to traefik
|
||||||
// using the given configuration channel.
|
// using the given configuration channel.
|
||||||
func (provider *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ types.Constraints) error {
|
func (provider *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ types.Constraints) error {
|
||||||
|
|
||||||
systemRouter := mux.NewRouter()
|
systemRouter := mux.NewRouter()
|
||||||
|
|
||||||
if provider.Path == "" {
|
|
||||||
provider.Path = "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
if provider.Path != "/" {
|
if provider.Path != "/" {
|
||||||
if provider.Path[len(provider.Path)-1:] != "/" {
|
|
||||||
provider.Path += "/"
|
|
||||||
}
|
|
||||||
systemRouter.Methods("GET").Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
systemRouter.Methods("GET").Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||||
http.Redirect(response, request, provider.Path, 302)
|
http.Redirect(response, request, provider.Path, 302)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
mkdocs>=0.16.1
|
mkdocs==0.16.3
|
||||||
pymdown-extensions>=1.4
|
pymdown-extensions>=1.4
|
||||||
mkdocs-bootswatch>=0.4.0
|
mkdocs-bootswatch>=0.4.0
|
||||||
mkdocs-material>=1.8.1
|
mkdocs-material>=1.8.1
|
||||||
|
|
|
@ -136,6 +136,57 @@ func TestPriorites(t *testing.T) {
|
||||||
assert.NotEqual(t, foobarMatcher.Handler, fooHandler, "Error matching priority")
|
assert.NotEqual(t, foobarMatcher.Handler, fooHandler, "Error matching priority")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHostRegexp(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
hostExp string
|
||||||
|
urls map[string]bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "capturing group",
|
||||||
|
hostExp: "{subdomain:(foo\\.)?bar\\.com}",
|
||||||
|
urls: map[string]bool{
|
||||||
|
"http://foo.bar.com": true,
|
||||||
|
"http://bar.com": true,
|
||||||
|
"http://fooubar.com": false,
|
||||||
|
"http://barucom": false,
|
||||||
|
"http://barcom": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "non capturing group",
|
||||||
|
hostExp: "{subdomain:(?:foo\\.)?bar\\.com}",
|
||||||
|
urls: map[string]bool{
|
||||||
|
"http://foo.bar.com": true,
|
||||||
|
"http://bar.com": true,
|
||||||
|
"http://fooubar.com": false,
|
||||||
|
"http://barucom": false,
|
||||||
|
"http://barcom": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
rls := &Rules{
|
||||||
|
route: &serverRoute{
|
||||||
|
route: &mux.Route{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rt := rls.hostRegexp(test.hostExp)
|
||||||
|
|
||||||
|
for testURL, match := range test.urls {
|
||||||
|
req := testhelpers.MustNewRequest(http.MethodGet, testURL, nil)
|
||||||
|
assert.Equal(t, match, rt.Match(req, &mux.RouteMatch{}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type fakeHandler struct {
|
type fakeHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,7 +338,7 @@ func (server *Server) listenProviders(stop chan bool) {
|
||||||
lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time)
|
lastReceivedConfigurationValue := lastReceivedConfiguration.Get().(time.Time)
|
||||||
providersThrottleDuration := time.Duration(server.globalConfiguration.ProvidersThrottleDuration)
|
providersThrottleDuration := time.Duration(server.globalConfiguration.ProvidersThrottleDuration)
|
||||||
if time.Now().After(lastReceivedConfigurationValue.Add(providersThrottleDuration)) {
|
if time.Now().After(lastReceivedConfigurationValue.Add(providersThrottleDuration)) {
|
||||||
log.Debugf("Last %s config received more than %s, OK", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration)
|
log.Debugf("Last %s config received more than %s, OK", configMsg.ProviderName, server.globalConfiguration.ProvidersThrottleDuration.String())
|
||||||
// last config received more than n s ago
|
// last config received more than n s ago
|
||||||
server.configurationValidatedChan <- configMsg
|
server.configurationValidatedChan <- configMsg
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
{{if hasServices $server}}
|
{{if hasServices $server}}
|
||||||
{{$services := getServiceNames $server}}
|
{{$services := getServiceNames $server}}
|
||||||
{{range $serviceIndex, $serviceName := $services}}
|
{{range $serviceIndex, $serviceName := $services}}
|
||||||
[backends.backend-{{getServiceBackend $server $serviceName}}.servers.service]
|
[backends.backend-{{getServiceBackend $server $serviceName}}.servers.service-{{$serverName}}]
|
||||||
url = "{{getServiceProtocol $server $serviceName}}://{{getIPAddress $server}}:{{getServicePort $server $serviceName}}"
|
url = "{{getServiceProtocol $server $serviceName}}://{{getIPAddress $server}}:{{getServicePort $server $serviceName}}"
|
||||||
weight = {{getServiceWeight $server $serviceName}}
|
weight = {{getServiceWeight $server $serviceName}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
5
vendor/github.com/containous/mux/doc.go
generated
vendored
5
vendor/github.com/containous/mux/doc.go
generated
vendored
|
@ -57,11 +57,6 @@ calling mux.Vars():
|
||||||
vars := mux.Vars(request)
|
vars := mux.Vars(request)
|
||||||
category := vars["category"]
|
category := vars["category"]
|
||||||
|
|
||||||
Note that if any capturing groups are present, mux will panic() during parsing. To prevent
|
|
||||||
this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to
|
|
||||||
"/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably
|
|
||||||
when capturing groups were present.
|
|
||||||
|
|
||||||
And this is all you need to know about the basic usage. More advanced options
|
And this is all you need to know about the basic usage. More advanced options
|
||||||
are explained below.
|
are explained below.
|
||||||
|
|
||||||
|
|
58
vendor/github.com/containous/mux/mux.go
generated
vendored
58
vendor/github.com/containous/mux/mux.go
generated
vendored
|
@ -11,7 +11,10 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrMethodMismatch = errors.New("method is not allowed")
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewRouter returns a new router instance.
|
// NewRouter returns a new router instance.
|
||||||
|
@ -40,6 +43,10 @@ func NewRouter() *Router {
|
||||||
type Router struct {
|
type Router struct {
|
||||||
// Configurable Handler to be used when no route matches.
|
// Configurable Handler to be used when no route matches.
|
||||||
NotFoundHandler http.Handler
|
NotFoundHandler http.Handler
|
||||||
|
|
||||||
|
// Configurable Handler to be used when the request method does not match the route.
|
||||||
|
MethodNotAllowedHandler http.Handler
|
||||||
|
|
||||||
// Parent route, if this is a subrouter.
|
// Parent route, if this is a subrouter.
|
||||||
parent parentRoute
|
parent parentRoute
|
||||||
// Routes to be matched, in order.
|
// Routes to be matched, in order.
|
||||||
|
@ -66,6 +73,11 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if match.MatchErr == ErrMethodMismatch && r.MethodNotAllowedHandler != nil {
|
||||||
|
match.Handler = r.MethodNotAllowedHandler
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Closest match for a router (includes sub-routers)
|
// Closest match for a router (includes sub-routers)
|
||||||
if r.NotFoundHandler != nil {
|
if r.NotFoundHandler != nil {
|
||||||
match.Handler = r.NotFoundHandler
|
match.Handler = r.NotFoundHandler
|
||||||
|
@ -82,7 +94,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
if !r.skipClean {
|
if !r.skipClean {
|
||||||
path := req.URL.Path
|
path := req.URL.Path
|
||||||
if r.useEncodedPath {
|
if r.useEncodedPath {
|
||||||
path = getPath(req)
|
path = req.URL.EscapedPath()
|
||||||
}
|
}
|
||||||
// Clean path to canonical form and redirect.
|
// Clean path to canonical form and redirect.
|
||||||
if p := cleanPath(path); p != path {
|
if p := cleanPath(path); p != path {
|
||||||
|
@ -106,9 +118,15 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
req = setVars(req, match.Vars)
|
req = setVars(req, match.Vars)
|
||||||
req = setCurrentRoute(req, match.Route)
|
req = setCurrentRoute(req, match.Route)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if handler == nil && match.MatchErr == ErrMethodMismatch {
|
||||||
|
handler = methodNotAllowedHandler()
|
||||||
|
}
|
||||||
|
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
handler = http.NotFoundHandler()
|
handler = http.NotFoundHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !r.KeepContext {
|
if !r.KeepContext {
|
||||||
defer contextClear(req)
|
defer contextClear(req)
|
||||||
}
|
}
|
||||||
|
@ -356,6 +374,11 @@ type RouteMatch struct {
|
||||||
Route *Route
|
Route *Route
|
||||||
Handler http.Handler
|
Handler http.Handler
|
||||||
Vars map[string]string
|
Vars map[string]string
|
||||||
|
|
||||||
|
// MatchErr is set to appropriate matching error
|
||||||
|
// It is set to ErrMethodMismatch if there is a mismatch in
|
||||||
|
// the request method and route method
|
||||||
|
MatchErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
type contextKey int
|
type contextKey int
|
||||||
|
@ -397,28 +420,6 @@ func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
|
||||||
// Helpers
|
// Helpers
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// getPath returns the escaped path if possible; doing what URL.EscapedPath()
|
|
||||||
// which was added in go1.5 does
|
|
||||||
func getPath(req *http.Request) string {
|
|
||||||
if req.RequestURI != "" {
|
|
||||||
// Extract the path from RequestURI (which is escaped unlike URL.Path)
|
|
||||||
// as detailed here as detailed in https://golang.org/pkg/net/url/#URL
|
|
||||||
// for < 1.5 server side workaround
|
|
||||||
// http://localhost/path/here?v=1 -> /path/here
|
|
||||||
path := req.RequestURI
|
|
||||||
path = strings.TrimPrefix(path, req.URL.Scheme+`://`)
|
|
||||||
path = strings.TrimPrefix(path, req.URL.Host)
|
|
||||||
if i := strings.LastIndex(path, "?"); i > -1 {
|
|
||||||
path = path[:i]
|
|
||||||
}
|
|
||||||
if i := strings.LastIndex(path, "#"); i > -1 {
|
|
||||||
path = path[:i]
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
return req.URL.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanPath returns the canonical path for p, eliminating . and .. elements.
|
// cleanPath returns the canonical path for p, eliminating . and .. elements.
|
||||||
// Borrowed from the net/http package.
|
// Borrowed from the net/http package.
|
||||||
func cleanPath(p string) string {
|
func cleanPath(p string) string {
|
||||||
|
@ -557,3 +558,12 @@ func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]s
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// methodNotAllowed replies to the request with an HTTP status code 405.
|
||||||
|
func methodNotAllowed(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// methodNotAllowedHandler returns a simple request handler
|
||||||
|
// that replies to each request with a status code 405.
|
||||||
|
func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) }
|
||||||
|
|
24
vendor/github.com/containous/mux/regexp.go
generated
vendored
24
vendor/github.com/containous/mux/regexp.go
generated
vendored
|
@ -109,13 +109,6 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
|
||||||
if errCompile != nil {
|
if errCompile != nil {
|
||||||
return nil, errCompile
|
return nil, errCompile
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for capturing groups which used to work in older versions
|
|
||||||
if reg.NumSubexp() != len(idxs)/2 {
|
|
||||||
panic(fmt.Sprintf("route %s contains capture groups in its regexp. ", template) +
|
|
||||||
"Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done!
|
// Done!
|
||||||
return &routeRegexp{
|
return &routeRegexp{
|
||||||
template: template,
|
template: template,
|
||||||
|
@ -141,7 +134,7 @@ type routeRegexp struct {
|
||||||
matchQuery bool
|
matchQuery bool
|
||||||
// The strictSlash value defined on the route, but disabled if PathPrefix was used.
|
// The strictSlash value defined on the route, but disabled if PathPrefix was used.
|
||||||
strictSlash bool
|
strictSlash bool
|
||||||
// Determines whether to use encoded path from getPath function or unencoded
|
// Determines whether to use encoded req.URL.EnscapedPath() or unencoded
|
||||||
// req.URL.Path for path matching
|
// req.URL.Path for path matching
|
||||||
useEncodedPath bool
|
useEncodedPath bool
|
||||||
// Expanded regexp.
|
// Expanded regexp.
|
||||||
|
@ -162,7 +155,7 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
}
|
}
|
||||||
path := req.URL.Path
|
path := req.URL.Path
|
||||||
if r.useEncodedPath {
|
if r.useEncodedPath {
|
||||||
path = getPath(req)
|
path = req.URL.EscapedPath()
|
||||||
}
|
}
|
||||||
return r.regexp.MatchString(path)
|
return r.regexp.MatchString(path)
|
||||||
}
|
}
|
||||||
|
@ -272,7 +265,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
|
||||||
}
|
}
|
||||||
path := req.URL.Path
|
path := req.URL.Path
|
||||||
if r.useEncodedPath {
|
if r.useEncodedPath {
|
||||||
path = getPath(req)
|
path = req.URL.EscapedPath()
|
||||||
}
|
}
|
||||||
// Store path variables.
|
// Store path variables.
|
||||||
if v.path != nil {
|
if v.path != nil {
|
||||||
|
@ -320,7 +313,14 @@ func getHost(r *http.Request) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractVars(input string, matches []int, names []string, output map[string]string) {
|
func extractVars(input string, matches []int, names []string, output map[string]string) {
|
||||||
for i, name := range names {
|
matchesCount := 0
|
||||||
output[name] = input[matches[2*i+2]:matches[2*i+3]]
|
prevEnd := -1
|
||||||
|
for i := 2; i < len(matches) && matchesCount < len(names); i += 2 {
|
||||||
|
if prevEnd < matches[i+1] {
|
||||||
|
value := input[matches[i]:matches[i+1]]
|
||||||
|
output[names[matchesCount]] = value
|
||||||
|
prevEnd = matches[i+1]
|
||||||
|
matchesCount++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
vendor/github.com/containous/mux/route.go
generated
vendored
54
vendor/github.com/containous/mux/route.go
generated
vendored
|
@ -54,12 +54,27 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
if r.buildOnly || r.err != nil {
|
if r.buildOnly || r.err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var matchErr error
|
||||||
|
|
||||||
// Match everything.
|
// Match everything.
|
||||||
for _, m := range r.matchers {
|
for _, m := range r.matchers {
|
||||||
if matched := m.Match(req, match); !matched {
|
if matched := m.Match(req, match); !matched {
|
||||||
|
if _, ok := m.(methodMatcher); ok {
|
||||||
|
matchErr = ErrMethodMismatch
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matchErr = nil
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if matchErr != nil {
|
||||||
|
match.MatchErr = matchErr
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
match.MatchErr = nil
|
||||||
// Yay, we have a match. Let's collect some info about it.
|
// Yay, we have a match. Let's collect some info about it.
|
||||||
if match.Route == nil {
|
if match.Route == nil {
|
||||||
match.Route = r
|
match.Route = r
|
||||||
|
@ -70,6 +85,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
if match.Vars == nil {
|
if match.Vars == nil {
|
||||||
match.Vars = make(map[string]string)
|
match.Vars = make(map[string]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set variables.
|
// Set variables.
|
||||||
if r.regexp != nil {
|
if r.regexp != nil {
|
||||||
r.regexp.setMatch(req, match, r)
|
r.regexp.setMatch(req, match, r)
|
||||||
|
@ -607,6 +623,44 @@ func (r *Route) GetPathRegexp() (string, error) {
|
||||||
return r.regexp.path.regexp.String(), nil
|
return r.regexp.path.regexp.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetQueriesRegexp returns the expanded regular expressions used to match the
|
||||||
|
// route queries.
|
||||||
|
// This is useful for building simple REST API documentation and for instrumentation
|
||||||
|
// against third-party services.
|
||||||
|
// An empty list will be returned if the route does not have queries.
|
||||||
|
func (r *Route) GetQueriesRegexp() ([]string, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
if r.regexp == nil || r.regexp.queries == nil {
|
||||||
|
return nil, errors.New("mux: route doesn't have queries")
|
||||||
|
}
|
||||||
|
var queries []string
|
||||||
|
for _, query := range r.regexp.queries {
|
||||||
|
queries = append(queries, query.regexp.String())
|
||||||
|
}
|
||||||
|
return queries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQueriesTemplates returns the templates used to build the
|
||||||
|
// query matching.
|
||||||
|
// This is useful for building simple REST API documentation and for instrumentation
|
||||||
|
// against third-party services.
|
||||||
|
// An empty list will be returned if the route does not define queries.
|
||||||
|
func (r *Route) GetQueriesTemplates() ([]string, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
if r.regexp == nil || r.regexp.queries == nil {
|
||||||
|
return nil, errors.New("mux: route doesn't have queries")
|
||||||
|
}
|
||||||
|
var queries []string
|
||||||
|
for _, query := range r.regexp.queries {
|
||||||
|
queries = append(queries, query.template)
|
||||||
|
}
|
||||||
|
return queries, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetMethods returns the methods the route matches against
|
// GetMethods returns the methods the route matches against
|
||||||
// This is useful for building simple REST API documentation and for instrumentation
|
// This is useful for building simple REST API documentation and for instrumentation
|
||||||
// against third-party services.
|
// against third-party services.
|
||||||
|
|
7
vendor/github.com/vulcand/oxy/forward/fwd.go
generated
vendored
7
vendor/github.com/vulcand/oxy/forward/fwd.go
generated
vendored
|
@ -190,7 +190,9 @@ func (f *httpForwarder) serveHTTP(w http.ResponseWriter, req *http.Request, ctx
|
||||||
stream = contentType == "text/event-stream"
|
stream = contentType == "text/event-stream"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
written, err := io.Copy(newResponseFlusher(w, stream), response.Body)
|
|
||||||
|
flush := stream || req.ProtoMajor == 2
|
||||||
|
written, err := io.Copy(newResponseFlusher(w, flush), response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.log.Errorf("Error copying upstream response body: %v", err)
|
ctx.log.Errorf("Error copying upstream response body: %v", err)
|
||||||
ctx.errHandler.ServeHTTP(w, req, err)
|
ctx.errHandler.ServeHTTP(w, req, err)
|
||||||
|
@ -264,7 +266,8 @@ func (f *websocketForwarder) serveHTTP(w http.ResponseWriter, req *http.Request,
|
||||||
|
|
||||||
dialer := websocket.DefaultDialer
|
dialer := websocket.DefaultDialer
|
||||||
if outReq.URL.Scheme == "wss" && f.TLSClientConfig != nil {
|
if outReq.URL.Scheme == "wss" && f.TLSClientConfig != nil {
|
||||||
dialer.TLSClientConfig = f.TLSClientConfig
|
dialer.TLSClientConfig = f.TLSClientConfig.Clone()
|
||||||
|
dialer.TLSClientConfig.NextProtos = []string{"http/1.1"}
|
||||||
}
|
}
|
||||||
targetConn, resp, err := dialer.Dial(outReq.URL.String(), outReq.Header)
|
targetConn, resp, err := dialer.Dial(outReq.URL.String(), outReq.Header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
10
vendor/github.com/vulcand/oxy/forward/headers.go
generated
vendored
10
vendor/github.com/vulcand/oxy/forward/headers.go
generated
vendored
|
@ -6,6 +6,7 @@ const (
|
||||||
XForwardedHost = "X-Forwarded-Host"
|
XForwardedHost = "X-Forwarded-Host"
|
||||||
XForwardedPort = "X-Forwarded-Port"
|
XForwardedPort = "X-Forwarded-Port"
|
||||||
XForwardedServer = "X-Forwarded-Server"
|
XForwardedServer = "X-Forwarded-Server"
|
||||||
|
XRealIp = "X-Real-Ip"
|
||||||
Connection = "Connection"
|
Connection = "Connection"
|
||||||
KeepAlive = "Keep-Alive"
|
KeepAlive = "Keep-Alive"
|
||||||
ProxyAuthenticate = "Proxy-Authenticate"
|
ProxyAuthenticate = "Proxy-Authenticate"
|
||||||
|
@ -50,3 +51,12 @@ var WebsocketUpgradeHeaders = []string{
|
||||||
Connection,
|
Connection,
|
||||||
SecWebsocketAccept,
|
SecWebsocketAccept,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var XHeaders = []string{
|
||||||
|
XForwardedProto,
|
||||||
|
XForwardedFor,
|
||||||
|
XForwardedHost,
|
||||||
|
XForwardedPort,
|
||||||
|
XForwardedServer,
|
||||||
|
XRealIp,
|
||||||
|
}
|
||||||
|
|
54
vendor/github.com/vulcand/oxy/forward/rewrite.go
generated
vendored
54
vendor/github.com/vulcand/oxy/forward/rewrite.go
generated
vendored
|
@ -15,30 +15,36 @@ type HeaderRewriter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rw *HeaderRewriter) Rewrite(req *http.Request) {
|
func (rw *HeaderRewriter) Rewrite(req *http.Request) {
|
||||||
|
if !rw.TrustForwardHeader {
|
||||||
|
utils.RemoveHeaders(req.Header, XHeaders...)
|
||||||
|
}
|
||||||
|
|
||||||
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
||||||
if rw.TrustForwardHeader {
|
if prior, ok := req.Header[XForwardedFor]; ok {
|
||||||
if prior, ok := req.Header[XForwardedFor]; ok {
|
req.Header.Set(XForwardedFor, strings.Join(prior, ", ")+", "+clientIP)
|
||||||
clientIP = strings.Join(prior, ", ") + ", " + clientIP
|
} else {
|
||||||
}
|
req.Header.Set(XForwardedFor, clientIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Header.Get(XRealIp) == "" {
|
||||||
|
req.Header.Set(XRealIp, clientIP)
|
||||||
}
|
}
|
||||||
req.Header.Set(XForwardedFor, clientIP)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if xfp := req.Header.Get(XForwardedProto); xfp != "" && rw.TrustForwardHeader {
|
xfProto := req.Header.Get(XForwardedProto)
|
||||||
req.Header.Set(XForwardedProto, xfp)
|
if xfProto == "" {
|
||||||
} else if req.TLS != nil {
|
if req.TLS != nil {
|
||||||
req.Header.Set(XForwardedProto, "https")
|
req.Header.Set(XForwardedProto, "https")
|
||||||
} else {
|
} else {
|
||||||
req.Header.Set(XForwardedProto, "http")
|
req.Header.Set(XForwardedProto, "http")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if xfp := req.Header.Get(XForwardedPort); xfp != "" && rw.TrustForwardHeader {
|
if xfp := req.Header.Get(XForwardedPort); xfp == "" {
|
||||||
req.Header.Set(XForwardedPort, xfp)
|
req.Header.Set(XForwardedPort, forwardedPort(req))
|
||||||
}
|
}
|
||||||
|
|
||||||
if xfh := req.Header.Get(XForwardedHost); xfh != "" && rw.TrustForwardHeader {
|
if xfHost := req.Header.Get(XForwardedHost); xfHost == "" && req.Host != "" {
|
||||||
req.Header.Set(XForwardedHost, xfh)
|
|
||||||
} else if req.Host != "" {
|
|
||||||
req.Header.Set(XForwardedHost, req.Host)
|
req.Header.Set(XForwardedHost, req.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,3 +56,19 @@ func (rw *HeaderRewriter) Rewrite(req *http.Request) {
|
||||||
// connection, regardless of what the client sent to us.
|
// connection, regardless of what the client sent to us.
|
||||||
utils.RemoveHeaders(req.Header, HopHeaders...)
|
utils.RemoveHeaders(req.Header, HopHeaders...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func forwardedPort(req *http.Request) string {
|
||||||
|
if req == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, port, err := net.SplitHostPort(req.Host); err == nil && port != "" {
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.TLS != nil {
|
||||||
|
return "443"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "80"
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue